@pure-ds/core 0.6.3 → 0.6.5
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/.cursorrules +2 -1
- package/.github/copilot-instructions.md +2 -1
- package/custom-elements.json +329 -17
- package/dist/types/public/assets/js/pds-manager.d.ts +1 -1
- package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-live-edit.d.ts +28 -8
- package/dist/types/public/assets/pds/components/pds-live-edit.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
- package/package.json +1 -1
- package/public/assets/js/app.js +57 -15
- package/public/assets/js/pds-manager.js +162 -163
- package/public/assets/pds/components/pds-live-edit.js +1939 -1554
- package/public/assets/pds/custom-elements.json +329 -17
- package/public/assets/pds/pds-css-complete.json +1 -1
- package/public/assets/pds/vscode-custom-data.json +9 -7
- package/src/js/pds-core/pds-generator.js +80 -67
|
@@ -1,1555 +1,1940 @@
|
|
|
1
|
-
const PDS = globalThis.PDS;
|
|
2
|
-
|
|
3
|
-
const EDITOR_TAG = "pds-live-edit";
|
|
4
|
-
const STYLE_ID = "pds-live-editor-styles";
|
|
5
|
-
const TARGET_ATTR = "data-pds-live-target";
|
|
6
|
-
const DROPDOWN_CLASS = "pds-live-editor-dropdown";
|
|
7
|
-
const MARKER_CLASS = "pds-live-editor-marker";
|
|
8
|
-
|
|
9
|
-
const CATEGORY_ICONS = {
|
|
10
|
-
colors: "palette",
|
|
11
|
-
typography: "text-aa",
|
|
12
|
-
spatialRhythm: "grid-four",
|
|
13
|
-
shape: "circle",
|
|
14
|
-
layout: "grid-four",
|
|
15
|
-
behavior: "sparkle",
|
|
16
|
-
layers: "squares-four",
|
|
17
|
-
icons: "sparkle",
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const QUICK_RULES = [
|
|
21
|
-
{
|
|
22
|
-
selector: "button, .btn-primary, .btn-secondary, .btn-outline, .btn-sm, .btn-xs, .btn-lg",
|
|
23
|
-
paths: [
|
|
24
|
-
"colors.primary",
|
|
25
|
-
"shape.radiusSize",
|
|
26
|
-
"spatialRhythm.buttonPadding",
|
|
27
|
-
"layout.buttonMinHeight",
|
|
28
|
-
"behavior.transitionSpeed",
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
selector: "input, textarea, select, label",
|
|
33
|
-
paths: [
|
|
34
|
-
"colors.secondary",
|
|
35
|
-
"shape.radiusSize",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"color",
|
|
68
|
-
"
|
|
69
|
-
"border-
|
|
70
|
-
"border-
|
|
71
|
-
"border-
|
|
72
|
-
"border-
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"font-
|
|
80
|
-
"font-
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"padding
|
|
86
|
-
"padding-
|
|
87
|
-
"padding-
|
|
88
|
-
"padding-
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"min-
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"layout.
|
|
113
|
-
"layout.
|
|
114
|
-
"layout.
|
|
115
|
-
"layout.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
"
|
|
120
|
-
"layout.
|
|
121
|
-
"
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
]);
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
.${DROPDOWN_CLASS} .pds-live-editor-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (!current
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
.
|
|
421
|
-
.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
) {
|
|
748
|
-
|
|
749
|
-
}
|
|
750
|
-
if (
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
};
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
})
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
if (
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
this.
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
if (
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1
|
+
const PDS = globalThis.PDS;
|
|
2
|
+
|
|
3
|
+
const EDITOR_TAG = "pds-live-edit";
|
|
4
|
+
const STYLE_ID = "pds-live-editor-styles";
|
|
5
|
+
const TARGET_ATTR = "data-pds-live-target";
|
|
6
|
+
const DROPDOWN_CLASS = "pds-live-editor-dropdown";
|
|
7
|
+
const MARKER_CLASS = "pds-live-editor-marker";
|
|
8
|
+
|
|
9
|
+
const CATEGORY_ICONS = {
|
|
10
|
+
colors: "palette",
|
|
11
|
+
typography: "text-aa",
|
|
12
|
+
spatialRhythm: "grid-four",
|
|
13
|
+
shape: "circle",
|
|
14
|
+
layout: "grid-four",
|
|
15
|
+
behavior: "sparkle",
|
|
16
|
+
layers: "squares-four",
|
|
17
|
+
icons: "sparkle",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const QUICK_RULES = [
|
|
21
|
+
{
|
|
22
|
+
selector: "button, .btn-primary, .btn-secondary, .btn-outline, .btn-sm, .btn-xs, .btn-lg",
|
|
23
|
+
paths: [
|
|
24
|
+
"colors.primary",
|
|
25
|
+
"shape.radiusSize",
|
|
26
|
+
"spatialRhythm.buttonPadding",
|
|
27
|
+
"layout.buttonMinHeight",
|
|
28
|
+
"behavior.transitionSpeed",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
selector: "input, textarea, select, label",
|
|
33
|
+
paths: [
|
|
34
|
+
"colors.secondary",
|
|
35
|
+
"shape.radiusSize",
|
|
36
|
+
"shape.borderWidth",
|
|
37
|
+
"spatialRhythm.inputPadding",
|
|
38
|
+
"layout.inputMinHeight",
|
|
39
|
+
"typography.fontFamilyBody",
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
selector: ".card, .surface-base, .surface-elevated, .surface-sunken, .surface-subtle",
|
|
44
|
+
paths: [
|
|
45
|
+
"colors.background",
|
|
46
|
+
"shape.radiusSize",
|
|
47
|
+
"layers.baseShadowOpacity",
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
selector: "nav, menu",
|
|
52
|
+
paths: ["colors.background", "typography.fontFamilyBody", "spatialRhythm.baseUnit"],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
selector: "pds-icon",
|
|
56
|
+
paths: ["icons.defaultSize", "icons.weight"],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const DEFAULT_QUICK_PATHS = [
|
|
61
|
+
"colors.primary",
|
|
62
|
+
"typography.fontFamilyBody",
|
|
63
|
+
"shape.radiusSize",
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const QUICK_STYLE_PROPERTIES = [
|
|
67
|
+
"background-color",
|
|
68
|
+
"color",
|
|
69
|
+
"border-color",
|
|
70
|
+
"border-top-color",
|
|
71
|
+
"border-right-color",
|
|
72
|
+
"border-bottom-color",
|
|
73
|
+
"border-left-color",
|
|
74
|
+
"outline-color",
|
|
75
|
+
"box-shadow",
|
|
76
|
+
"text-shadow",
|
|
77
|
+
"fill",
|
|
78
|
+
"stroke",
|
|
79
|
+
"font-family",
|
|
80
|
+
"font-size",
|
|
81
|
+
"font-weight",
|
|
82
|
+
"letter-spacing",
|
|
83
|
+
"line-height",
|
|
84
|
+
"border-radius",
|
|
85
|
+
"padding",
|
|
86
|
+
"padding-top",
|
|
87
|
+
"padding-right",
|
|
88
|
+
"padding-bottom",
|
|
89
|
+
"padding-left",
|
|
90
|
+
"gap",
|
|
91
|
+
"row-gap",
|
|
92
|
+
"column-gap",
|
|
93
|
+
"min-height",
|
|
94
|
+
"min-width",
|
|
95
|
+
"height",
|
|
96
|
+
"width",
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const INLINE_VAR_REGEX = /var\(\s*(--[^)\s,]+)\s*/g;
|
|
100
|
+
const CUSTOM_PROP_REGEX = /--.+/;
|
|
101
|
+
const COLOR_VALUE_REGEX = /#(?:[0-9a-f]{3,8})\b|rgba?\([^)]*\)|hsla?\([^)]*\)/gi;
|
|
102
|
+
|
|
103
|
+
const ENUM_FIELD_OPTIONS = {
|
|
104
|
+
"shape.borderWidth": ["hairline", "thin", "medium", "thick"],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
let cachedTokenIndex = null;
|
|
108
|
+
let cachedTokenIndexMeta = null;
|
|
109
|
+
let colorNormalizer = null;
|
|
110
|
+
|
|
111
|
+
const GLOBAL_LAYOUT_PATHS = new Set([
|
|
112
|
+
"layout.maxWidth",
|
|
113
|
+
"layout.maxWidths",
|
|
114
|
+
"layout.breakpoints",
|
|
115
|
+
"layout.containerMaxWidth",
|
|
116
|
+
"layout.containerPadding",
|
|
117
|
+
"layout.gridColumns",
|
|
118
|
+
"layout.gridGutter",
|
|
119
|
+
"layout.densityCompact",
|
|
120
|
+
"layout.densityNormal",
|
|
121
|
+
"layout.densityComfortable",
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
const FORM_CONTEXT_PATHS = new Set([
|
|
125
|
+
"spatialRhythm.inputPadding",
|
|
126
|
+
"layout.inputMinHeight",
|
|
127
|
+
"behavior.focusRingWidth",
|
|
128
|
+
"behavior.focusRingOpacity",
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
const SURFACE_CONTEXT_PATHS = new Set([
|
|
132
|
+
"colors.background",
|
|
133
|
+
"layers.baseShadowOpacity",
|
|
134
|
+
"layout.baseShadowOpacity",
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
const DARK_MODE_PATH_MARKER = ".darkMode.";
|
|
138
|
+
const QUICK_EDIT_LIMIT = 4;
|
|
139
|
+
const DROPDOWN_VIEWPORT_PADDING = 8;
|
|
140
|
+
|
|
141
|
+
function isHoverCapable() {
|
|
142
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
143
|
+
return window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function ensureStyles() {
|
|
147
|
+
if (typeof document === "undefined") return;
|
|
148
|
+
if (document.getElementById(STYLE_ID)) return;
|
|
149
|
+
const style = document.createElement("style");
|
|
150
|
+
style.id = STYLE_ID;
|
|
151
|
+
style.textContent = `
|
|
152
|
+
${EDITOR_TAG} {
|
|
153
|
+
display: contents;
|
|
154
|
+
}
|
|
155
|
+
[${TARGET_ATTR}] {
|
|
156
|
+
position: relative;
|
|
157
|
+
outline: 2px solid var(--color-primary-500);
|
|
158
|
+
outline-offset: -2px;
|
|
159
|
+
}
|
|
160
|
+
[${TARGET_ATTR}]::after {
|
|
161
|
+
content: '';
|
|
162
|
+
position: absolute;
|
|
163
|
+
inset: 0;
|
|
164
|
+
background-color: var(--color-primary-500);
|
|
165
|
+
opacity: 0.08;
|
|
166
|
+
pointer-events: none;
|
|
167
|
+
z-index: var(--z-base);
|
|
168
|
+
}
|
|
169
|
+
.${DROPDOWN_CLASS} {
|
|
170
|
+
position: fixed;
|
|
171
|
+
top: var(--pds-live-edit-top, auto);
|
|
172
|
+
right: var(--pds-live-edit-right, auto);
|
|
173
|
+
bottom: var(--pds-live-edit-bottom, auto);
|
|
174
|
+
left: var(--pds-live-edit-left, auto);
|
|
175
|
+
z-index: var(--z-popover);
|
|
176
|
+
}
|
|
177
|
+
.${MARKER_CLASS} {
|
|
178
|
+
pointer-events: auto;
|
|
179
|
+
}
|
|
180
|
+
.context-edit {
|
|
181
|
+
min-width: 0;
|
|
182
|
+
min-height: 0;
|
|
183
|
+
width: var(--spacing-6);
|
|
184
|
+
height: var(--spacing-6);
|
|
185
|
+
padding: 0;
|
|
186
|
+
}
|
|
187
|
+
.${DROPDOWN_CLASS} menu {
|
|
188
|
+
min-width: max-content;
|
|
189
|
+
max-width: 350px;
|
|
190
|
+
}
|
|
191
|
+
.${DROPDOWN_CLASS} .pds-live-editor-menu {
|
|
192
|
+
padding: var(--spacing-1);
|
|
193
|
+
max-width: 350px;
|
|
194
|
+
padding-bottom: 0;
|
|
195
|
+
}
|
|
196
|
+
.${DROPDOWN_CLASS} .pds-live-editor-form-container {
|
|
197
|
+
padding-bottom: var(--spacing-2);
|
|
198
|
+
}
|
|
199
|
+
.${DROPDOWN_CLASS} .pds-live-editor-title {
|
|
200
|
+
display: block;
|
|
201
|
+
font-size: var(--font-size-sm);
|
|
202
|
+
font-weight: var(--font-weight-semibold);
|
|
203
|
+
margin-bottom: var(--spacing-2);
|
|
204
|
+
}
|
|
205
|
+
.${DROPDOWN_CLASS} .pds-live-editor-header {
|
|
206
|
+
display: flex;
|
|
207
|
+
align-items: center;
|
|
208
|
+
justify-content: space-between;
|
|
209
|
+
gap: var(--spacing-2);
|
|
210
|
+
}
|
|
211
|
+
.${DROPDOWN_CLASS} .pds-live-editor-debug {
|
|
212
|
+
font-size: var(--font-size-xs);
|
|
213
|
+
opacity: 0.7;
|
|
214
|
+
white-space: pre-wrap;
|
|
215
|
+
margin-top: var(--spacing-2);
|
|
216
|
+
}
|
|
217
|
+
.${DROPDOWN_CLASS} .pds-live-editor-menu input[type="color"] {
|
|
218
|
+
height: var(--spacing-6);
|
|
219
|
+
min-width: var(--spacing-9);
|
|
220
|
+
max-width: unset;
|
|
221
|
+
padding: 0;
|
|
222
|
+
border-radius: var(--radius-sm);
|
|
223
|
+
}
|
|
224
|
+
.${DROPDOWN_CLASS} .pds-live-editor-footer {
|
|
225
|
+
display: flex;
|
|
226
|
+
gap: var(--spacing-2);
|
|
227
|
+
padding: var(--spacing-2);
|
|
228
|
+
border-top: 1px solid var(--color-border);
|
|
229
|
+
background: var(--color-surface-base);
|
|
230
|
+
position: sticky;
|
|
231
|
+
justify-content: space-between;
|
|
232
|
+
bottom: 0;
|
|
233
|
+
z-index: 1;
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
document.head.appendChild(style);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function isSelectorSupported(selector) {
|
|
240
|
+
if (typeof selector !== "string" || !selector.trim()) return false;
|
|
241
|
+
if (typeof CSS !== "undefined" && typeof CSS.supports === "function") {
|
|
242
|
+
try {
|
|
243
|
+
return CSS.supports(`selector(${selector})`);
|
|
244
|
+
} catch (e) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (typeof document === "undefined") return false;
|
|
249
|
+
try {
|
|
250
|
+
document.querySelector(selector);
|
|
251
|
+
return true;
|
|
252
|
+
} catch (e) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function collectSelectors() {
|
|
258
|
+
const ontology = PDS?.ontology;
|
|
259
|
+
const selectors = new Set();
|
|
260
|
+
if (!ontology) return { selector: "", list: [] };
|
|
261
|
+
|
|
262
|
+
const addSelector = (selector) => {
|
|
263
|
+
if (typeof selector !== "string" || !selector.trim()) return;
|
|
264
|
+
if (isSelectorSupported(selector)) selectors.add(selector);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const addSelectorList = (list) => {
|
|
268
|
+
(list || []).forEach((selector) => addSelector(selector));
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const sections = [ontology.primitives, ontology.components, ontology.layoutPatterns];
|
|
272
|
+
sections.forEach((items) => {
|
|
273
|
+
if (!Array.isArray(items)) return;
|
|
274
|
+
items.forEach((item) => {
|
|
275
|
+
addSelectorList(item?.selectors || []);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
Object.values(ontology.utilities || {}).forEach((group) => {
|
|
280
|
+
if (!group || typeof group !== "object") return;
|
|
281
|
+
Object.values(group).forEach((list) => addSelectorList(list));
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
(ontology.enhancements || []).forEach((enhancer) => {
|
|
285
|
+
addSelector(enhancer?.selector);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
addSelector("body");
|
|
289
|
+
addSelector("[data-dropdown]");
|
|
290
|
+
addSelector("*");
|
|
291
|
+
|
|
292
|
+
return { selector: Array.from(selectors).join(", "), list: Array.from(selectors) };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function shallowClone(value) {
|
|
296
|
+
if (!value || typeof value !== "object") return value;
|
|
297
|
+
return Array.isArray(value) ? [...value] : { ...value };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function deepMerge(target = {}, source = {}) {
|
|
301
|
+
if (!source || typeof source !== "object") return target;
|
|
302
|
+
const out = Array.isArray(target) ? [...target] : { ...target };
|
|
303
|
+
for (const [key, value] of Object.entries(source)) {
|
|
304
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
305
|
+
out[key] = deepMerge(out[key] && typeof out[key] === "object" ? out[key] : {}, value);
|
|
306
|
+
} else {
|
|
307
|
+
out[key] = value;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return out;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function titleize(value) {
|
|
314
|
+
return String(value)
|
|
315
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
316
|
+
.replace(/[_-]+/g, " ")
|
|
317
|
+
.replace(/\s+/g, " ")
|
|
318
|
+
.trim()
|
|
319
|
+
.replace(/^./, (char) => char.toUpperCase());
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function getValueAtPath(obj, pathSegments) {
|
|
323
|
+
let current = obj;
|
|
324
|
+
for (const segment of pathSegments) {
|
|
325
|
+
if (!current || typeof current !== "object") return undefined;
|
|
326
|
+
current = current[segment];
|
|
327
|
+
}
|
|
328
|
+
return current;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function setValueAtPath(target, pathSegments, value) {
|
|
332
|
+
let current = target;
|
|
333
|
+
for (let i = 0; i < pathSegments.length; i += 1) {
|
|
334
|
+
const segment = pathSegments[i];
|
|
335
|
+
if (i === pathSegments.length - 1) {
|
|
336
|
+
current[segment] = value;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!current[segment] || typeof current[segment] !== "object") {
|
|
340
|
+
current[segment] = {};
|
|
341
|
+
}
|
|
342
|
+
current = current[segment];
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function setValueAtJsonPath(target, jsonPath, value) {
|
|
347
|
+
if (!jsonPath || typeof jsonPath !== "string") return;
|
|
348
|
+
const parts = jsonPath.replace(/^\//, "").split("/").filter(Boolean);
|
|
349
|
+
if (!parts.length) return;
|
|
350
|
+
let current = target;
|
|
351
|
+
parts.forEach((segment, index) => {
|
|
352
|
+
if (index === parts.length - 1) {
|
|
353
|
+
current[segment] = value;
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (!current[segment] || typeof current[segment] !== "object") {
|
|
357
|
+
current[segment] = {};
|
|
358
|
+
}
|
|
359
|
+
current = current[segment];
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function getEnumOptions(path) {
|
|
364
|
+
if (path === "shape.borderWidth") {
|
|
365
|
+
const enumKeys = Object.keys(PDS?.enums?.BorderWidths || {});
|
|
366
|
+
if (enumKeys.length) return enumKeys;
|
|
367
|
+
}
|
|
368
|
+
return ENUM_FIELD_OPTIONS[path] || null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function normalizeEnumValue(path, value) {
|
|
372
|
+
const options = getEnumOptions(path);
|
|
373
|
+
if (!options || !options.length) return value;
|
|
374
|
+
if (typeof value === "string" && options.includes(value)) return value;
|
|
375
|
+
|
|
376
|
+
if (path === "shape.borderWidth" && typeof value === "number") {
|
|
377
|
+
const source = PDS?.enums?.BorderWidths || {
|
|
378
|
+
hairline: 0.5,
|
|
379
|
+
thin: 1,
|
|
380
|
+
medium: 2,
|
|
381
|
+
thick: 3,
|
|
382
|
+
};
|
|
383
|
+
const found = Object.entries(source).find(([, num]) => Number(num) === Number(value));
|
|
384
|
+
if (found) return found[0];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return value;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function normalizePaths(paths) {
|
|
391
|
+
const relations = PDS?.configRelations || {};
|
|
392
|
+
const seen = new Set();
|
|
393
|
+
const filtered = [];
|
|
394
|
+
(paths || []).forEach((path) => {
|
|
395
|
+
if (!relations[path]) return;
|
|
396
|
+
if (seen.has(path)) return;
|
|
397
|
+
seen.add(path);
|
|
398
|
+
filtered.push(path);
|
|
399
|
+
});
|
|
400
|
+
return filtered;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function collectRelationPathsByCategory() {
|
|
404
|
+
const relations = PDS?.configRelations || {};
|
|
405
|
+
const result = {};
|
|
406
|
+
Object.keys(relations).forEach((path) => {
|
|
407
|
+
const [category] = path.split(".");
|
|
408
|
+
if (!category) return;
|
|
409
|
+
if (!result[category]) result[category] = [];
|
|
410
|
+
result[category].push(path);
|
|
411
|
+
});
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function collectPathsFromRelations(target) {
|
|
416
|
+
const relations = PDS?.configRelations || {};
|
|
417
|
+
const paths = [];
|
|
418
|
+
Object.entries(relations).forEach(([path, relation]) => {
|
|
419
|
+
const rules = relation?.rules || [];
|
|
420
|
+
if (!Array.isArray(rules) || !rules.length) return;
|
|
421
|
+
const matches = rules.some((rule) => {
|
|
422
|
+
const selectors = rule?.selectors || [];
|
|
423
|
+
return selectors.some((selector) => {
|
|
424
|
+
try {
|
|
425
|
+
return target.matches(selector) || Boolean(target.querySelector(selector));
|
|
426
|
+
} catch (e) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
if (matches) paths.push(path);
|
|
432
|
+
});
|
|
433
|
+
return paths;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function collectQuickRulePaths(target) {
|
|
437
|
+
return QUICK_RULES.filter((rule) => {
|
|
438
|
+
try {
|
|
439
|
+
return target.matches(rule.selector);
|
|
440
|
+
} catch (e) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
}).flatMap((rule) => rule.paths);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function pathExistsInDesign(path, design) {
|
|
447
|
+
if (!path || !design) return false;
|
|
448
|
+
const segments = path.split(".");
|
|
449
|
+
let current = design;
|
|
450
|
+
for (const segment of segments) {
|
|
451
|
+
if (!current || typeof current !== "object") return false;
|
|
452
|
+
if (!(segment in current)) return false;
|
|
453
|
+
current = current[segment];
|
|
454
|
+
}
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function filterPathsByContext(target, paths) {
|
|
459
|
+
if (!target || !paths.length) return paths;
|
|
460
|
+
const isGlobal = target.matches("body, main");
|
|
461
|
+
const isInForm = Boolean(target.closest("form, pds-form"));
|
|
462
|
+
const isOnSurface = Boolean(
|
|
463
|
+
target.closest(
|
|
464
|
+
".card, .surface-base, .surface-elevated, .surface-sunken, .surface-subtle"
|
|
465
|
+
)
|
|
466
|
+
);
|
|
467
|
+
const theme = getActiveTheme();
|
|
468
|
+
const design = PDS?.currentConfig?.design || {};
|
|
469
|
+
|
|
470
|
+
return paths.filter((path) => {
|
|
471
|
+
if (!theme.isDark && path.includes(DARK_MODE_PATH_MARKER)) return false;
|
|
472
|
+
if (path.startsWith("typography.") && !isGlobal) return false;
|
|
473
|
+
if (GLOBAL_LAYOUT_PATHS.has(path) && !isGlobal) return false;
|
|
474
|
+
if (FORM_CONTEXT_PATHS.has(path) && !isInForm) return false;
|
|
475
|
+
if (SURFACE_CONTEXT_PATHS.has(path) && !(isOnSurface || isGlobal)) return false;
|
|
476
|
+
// Opacity fields should never be under colors category
|
|
477
|
+
if (path.startsWith("colors.") && path.toLowerCase().includes("opacity")) return false;
|
|
478
|
+
// Always allow borderWidth in quick edit (design may be partial/override-only)
|
|
479
|
+
if (path === "shape.borderWidth") return true;
|
|
480
|
+
// Filter out paths that don't exist in the current design config
|
|
481
|
+
if (!pathExistsInDesign(path, design)) return false;
|
|
482
|
+
return true;
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function getActiveTheme() {
|
|
487
|
+
if (typeof document === "undefined") return { value: "light", isDark: false };
|
|
488
|
+
const attr = document.documentElement?.getAttribute("data-theme");
|
|
489
|
+
const value = attr === "dark" ? "dark" : "light";
|
|
490
|
+
return { value, isDark: value === "dark" };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function getSpacingOffset() {
|
|
494
|
+
if (typeof window === "undefined" || typeof document === "undefined") return 8;
|
|
495
|
+
const value = window
|
|
496
|
+
.getComputedStyle(document.documentElement)
|
|
497
|
+
.getPropertyValue("--spacing-2")
|
|
498
|
+
.trim();
|
|
499
|
+
const parsed = Number.parseFloat(value);
|
|
500
|
+
if (Number.isNaN(parsed)) return 8;
|
|
501
|
+
return parsed;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function ensureColorNormalizer() {
|
|
505
|
+
if (typeof document === "undefined") return null;
|
|
506
|
+
if (colorNormalizer && document.contains(colorNormalizer)) return colorNormalizer;
|
|
507
|
+
if (!document.body) return null;
|
|
508
|
+
const probe = document.createElement("span");
|
|
509
|
+
probe.setAttribute("data-pds-color-normalizer", "");
|
|
510
|
+
probe.style.position = "fixed";
|
|
511
|
+
probe.style.left = "-9999px";
|
|
512
|
+
probe.style.top = "0";
|
|
513
|
+
probe.style.opacity = "0";
|
|
514
|
+
probe.style.pointerEvents = "none";
|
|
515
|
+
document.body.appendChild(probe);
|
|
516
|
+
colorNormalizer = probe;
|
|
517
|
+
return colorNormalizer;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function normalizeCssColor(value) {
|
|
521
|
+
if (!value || typeof window === "undefined") return null;
|
|
522
|
+
const probe = ensureColorNormalizer();
|
|
523
|
+
if (!probe) return null;
|
|
524
|
+
probe.style.color = "";
|
|
525
|
+
probe.style.color = value;
|
|
526
|
+
return window.getComputedStyle(probe).color || null;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function rgbToHex(value) {
|
|
530
|
+
if (!value) return null;
|
|
531
|
+
const match = value.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
|
|
532
|
+
if (!match) return null;
|
|
533
|
+
const [r, g, b] = match.slice(1, 4).map((num) => {
|
|
534
|
+
const parsed = Number.parseInt(num, 10);
|
|
535
|
+
if (Number.isNaN(parsed)) return 0;
|
|
536
|
+
return Math.max(0, Math.min(255, parsed));
|
|
537
|
+
});
|
|
538
|
+
const hex = (channel) => channel.toString(16).padStart(2, "0");
|
|
539
|
+
return `#${hex(r)}${hex(g)}${hex(b)}`;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function normalizeHexColor(value) {
|
|
543
|
+
if (!value) return null;
|
|
544
|
+
const trimmed = value.trim();
|
|
545
|
+
if (!trimmed.startsWith("#")) return null;
|
|
546
|
+
if (trimmed.length === 4) {
|
|
547
|
+
const [r, g, b] = trimmed.slice(1).split("");
|
|
548
|
+
return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
|
|
549
|
+
}
|
|
550
|
+
if (trimmed.length === 7) return trimmed.toLowerCase();
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function toColorInputValue(value) {
|
|
555
|
+
if (!value) return value;
|
|
556
|
+
const normalizedHex = normalizeHexColor(value);
|
|
557
|
+
if (normalizedHex) return normalizedHex;
|
|
558
|
+
const normalized = normalizeCssColor(value) || value;
|
|
559
|
+
const hexValue = normalizeHexColor(normalized) || rgbToHex(normalized);
|
|
560
|
+
return hexValue || value;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function getCustomPropertyNames(style) {
|
|
564
|
+
const names = [];
|
|
565
|
+
if (!style) return names;
|
|
566
|
+
for (let i = 0; i < style.length; i += 1) {
|
|
567
|
+
const name = style[i];
|
|
568
|
+
if (name && name.startsWith("--")) names.push(name);
|
|
569
|
+
}
|
|
570
|
+
return names;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function makeTokenMatchers(relations) {
|
|
574
|
+
const matchers = [];
|
|
575
|
+
Object.entries(relations || {}).forEach(([path, relation]) => {
|
|
576
|
+
const tokens = relation?.tokens || [];
|
|
577
|
+
if (!Array.isArray(tokens)) return;
|
|
578
|
+
tokens.forEach((pattern) => {
|
|
579
|
+
if (!pattern || typeof pattern !== "string") return;
|
|
580
|
+
const escaped = pattern
|
|
581
|
+
.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&")
|
|
582
|
+
.replace(/\*/g, ".*");
|
|
583
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
584
|
+
matchers.push({ path, regex });
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
return matchers;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function addToValueMap(map, key, value) {
|
|
591
|
+
if (!key) return;
|
|
592
|
+
const entry = map.get(key);
|
|
593
|
+
if (entry) {
|
|
594
|
+
entry.add(value);
|
|
595
|
+
} else {
|
|
596
|
+
map.set(key, new Set([value]));
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function getTokenIndex() {
|
|
601
|
+
if (typeof window === "undefined" || typeof document === "undefined") return null;
|
|
602
|
+
const relations = PDS?.configRelations || {};
|
|
603
|
+
const relationCount = Object.keys(relations).length;
|
|
604
|
+
const root = document.documentElement;
|
|
605
|
+
if (!root) return null;
|
|
606
|
+
const rootStyle = window.getComputedStyle(root);
|
|
607
|
+
const bodyStyle = document.body ? window.getComputedStyle(document.body) : null;
|
|
608
|
+
const customProps = Array.from(
|
|
609
|
+
new Set([...
|
|
610
|
+
getCustomPropertyNames(rootStyle),
|
|
611
|
+
...getCustomPropertyNames(bodyStyle),
|
|
612
|
+
])
|
|
613
|
+
);
|
|
614
|
+
const meta = { relationCount, propCount: customProps.length };
|
|
615
|
+
if (cachedTokenIndex && cachedTokenIndexMeta) {
|
|
616
|
+
if (
|
|
617
|
+
cachedTokenIndexMeta.relationCount === meta.relationCount &&
|
|
618
|
+
cachedTokenIndexMeta.propCount === meta.propCount
|
|
619
|
+
) {
|
|
620
|
+
return cachedTokenIndex;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const matchers = makeTokenMatchers(relations);
|
|
625
|
+
const varToPaths = new Map();
|
|
626
|
+
const valueToVars = new Map();
|
|
627
|
+
const colorToVars = new Map();
|
|
628
|
+
|
|
629
|
+
const getVarValue = (varName) => {
|
|
630
|
+
const rootValue = rootStyle.getPropertyValue(varName).trim();
|
|
631
|
+
if (rootValue) return rootValue;
|
|
632
|
+
if (!bodyStyle) return "";
|
|
633
|
+
return bodyStyle.getPropertyValue(varName).trim();
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
customProps.forEach((varName) => {
|
|
637
|
+
const varValue = getVarValue(varName);
|
|
638
|
+
if (varValue) {
|
|
639
|
+
addToValueMap(valueToVars, varValue, varName);
|
|
640
|
+
const normalizedColor = normalizeCssColor(varValue);
|
|
641
|
+
if (normalizedColor) addToValueMap(colorToVars, normalizedColor, varName);
|
|
642
|
+
}
|
|
643
|
+
matchers.forEach(({ path, regex }) => {
|
|
644
|
+
if (regex.test(varName)) {
|
|
645
|
+
addToValueMap(varToPaths, varName, path);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
cachedTokenIndex = { varToPaths, valueToVars, colorToVars, matchers, getVarValue };
|
|
651
|
+
cachedTokenIndexMeta = meta;
|
|
652
|
+
return cachedTokenIndex;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function extractAllVarRefs(text) {
|
|
656
|
+
const vars = new Set();
|
|
657
|
+
if (!text) return vars;
|
|
658
|
+
// Extract all custom property names (--*) from text, including nested var() fallbacks
|
|
659
|
+
const customPropMatches = text.matchAll(/--[a-zA-Z0-9_-]+/g);
|
|
660
|
+
for (const match of customPropMatches) {
|
|
661
|
+
vars.add(match[0]);
|
|
662
|
+
}
|
|
663
|
+
return vars;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function collectVarRefsFromInline(element) {
|
|
667
|
+
const vars = new Set();
|
|
668
|
+
if (!element || typeof element.getAttribute !== "function") return vars;
|
|
669
|
+
const styleAttr = element.getAttribute("style") || "";
|
|
670
|
+
if (!styleAttr) return vars;
|
|
671
|
+
return extractAllVarRefs(styleAttr);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function collectScanTargets(target, limit = 120) {
|
|
675
|
+
const nodes = [target];
|
|
676
|
+
if (!target || typeof target.querySelectorAll !== "function") return nodes;
|
|
677
|
+
const descendants = Array.from(target.querySelectorAll("*"));
|
|
678
|
+
if (descendants.length <= limit) return nodes.concat(descendants);
|
|
679
|
+
return nodes.concat(descendants.slice(0, limit));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function collectVarRefsFromMatchingRules(element) {
|
|
683
|
+
const vars = new Set();
|
|
684
|
+
if (!element || typeof window === "undefined") return vars;
|
|
685
|
+
|
|
686
|
+
try {
|
|
687
|
+
// Scan all stylesheets for rules that match this element
|
|
688
|
+
const sheets = Array.from(document.styleSheets);
|
|
689
|
+
|
|
690
|
+
for (const sheet of sheets) {
|
|
691
|
+
try {
|
|
692
|
+
// Skip external sheets due to CORS
|
|
693
|
+
const rules = sheet.cssRules || sheet.rules;
|
|
694
|
+
if (!rules) continue;
|
|
695
|
+
|
|
696
|
+
for (const rule of rules) {
|
|
697
|
+
// Check CSSStyleRule (ignoring @media, @keyframes, etc for now)
|
|
698
|
+
if (rule.type === CSSRule.STYLE_RULE) {
|
|
699
|
+
try {
|
|
700
|
+
if (element.matches(rule.selectorText)) {
|
|
701
|
+
// Extract var refs from all properties in this rule
|
|
702
|
+
const cssText = rule.style.cssText;
|
|
703
|
+
if (cssText) {
|
|
704
|
+
const extracted = extractAllVarRefs(cssText);
|
|
705
|
+
extracted.forEach(v => vars.add(v));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
} catch (e) {
|
|
709
|
+
// Invalid selector or matching error
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
} catch (e) {
|
|
714
|
+
// CORS or other sheet access error
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
} catch (e) {
|
|
718
|
+
// Fallback silently
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
return vars;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function collectPathsFromComputedStyles(target) {
|
|
725
|
+
const index = getTokenIndex();
|
|
726
|
+
if (!index || !target || typeof window === "undefined") return [];
|
|
727
|
+
const { varToPaths, valueToVars, colorToVars, matchers, getVarValue } = index;
|
|
728
|
+
const varsSeen = new Set();
|
|
729
|
+
const varsOrdered = [];
|
|
730
|
+
const scanTargets = collectScanTargets(target);
|
|
731
|
+
const addVarName = (name) => {
|
|
732
|
+
if (!name || varsSeen.has(name)) return;
|
|
733
|
+
varsSeen.add(name);
|
|
734
|
+
varsOrdered.push(name);
|
|
735
|
+
};
|
|
736
|
+
const addVarSet = (set) => {
|
|
737
|
+
set.forEach((name) => addVarName(name));
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
scanTargets.forEach((node) => {
|
|
741
|
+
addVarSet(collectVarRefsFromInline(node));
|
|
742
|
+
addVarSet(collectVarRefsFromMatchingRules(node));
|
|
743
|
+
|
|
744
|
+
let style = null;
|
|
745
|
+
try {
|
|
746
|
+
style = window.getComputedStyle(node);
|
|
747
|
+
} catch (e) {
|
|
748
|
+
style = null;
|
|
749
|
+
}
|
|
750
|
+
if (!style) return;
|
|
751
|
+
|
|
752
|
+
// Extract var refs from all properties (custom AND standard)
|
|
753
|
+
// We scan custom properties to find nested var() chains
|
|
754
|
+
// We scan standard properties to find var() usage
|
|
755
|
+
// We do NOT add custom property names themselves - those are definitions, not usage
|
|
756
|
+
for (let i = 0; i < style.length; i += 1) {
|
|
757
|
+
const propName = style[i];
|
|
758
|
+
const propValue = style.getPropertyValue(propName);
|
|
759
|
+
if (propValue) {
|
|
760
|
+
// Extract all var() references including nested fallbacks
|
|
761
|
+
addVarSet(extractAllVarRefs(propValue));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
QUICK_STYLE_PROPERTIES.forEach((prop) => {
|
|
766
|
+
const value = style.getPropertyValue(prop);
|
|
767
|
+
if (!value) return;
|
|
768
|
+
const trimmed = value.trim();
|
|
769
|
+
|
|
770
|
+
// Extract var refs from the value (handles var() with fallbacks)
|
|
771
|
+
addVarSet(extractAllVarRefs(trimmed));
|
|
772
|
+
|
|
773
|
+
if (trimmed && valueToVars.has(trimmed)) {
|
|
774
|
+
valueToVars.get(trimmed).forEach((varName) => addVarName(varName));
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const colors = trimmed.match(COLOR_VALUE_REGEX) || [];
|
|
778
|
+
colors.forEach((color) => {
|
|
779
|
+
const normalized = normalizeCssColor(color) || color;
|
|
780
|
+
if (colorToVars.has(normalized)) {
|
|
781
|
+
colorToVars.get(normalized).forEach((varName) => addVarName(varName));
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
const paths = [];
|
|
788
|
+
const seenPaths = new Set();
|
|
789
|
+
const hints = {};
|
|
790
|
+
const addPath = (path) => {
|
|
791
|
+
if (!path || seenPaths.has(path)) return;
|
|
792
|
+
seenPaths.add(path);
|
|
793
|
+
paths.push(path);
|
|
794
|
+
};
|
|
795
|
+
const addHint = (path, varName) => {
|
|
796
|
+
if (!path || !varName) return;
|
|
797
|
+
if (!path.startsWith("colors.")) return;
|
|
798
|
+
if (path.includes(DARK_MODE_PATH_MARKER) && !getActiveTheme().isDark) return;
|
|
799
|
+
if (hints[path]) return;
|
|
800
|
+
const rawValue = getVarValue ? getVarValue(varName) : "";
|
|
801
|
+
if (!rawValue) return;
|
|
802
|
+
const normalized = toColorInputValue(rawValue);
|
|
803
|
+
hints[path] = normalized;
|
|
804
|
+
};
|
|
805
|
+
const matchVarName = (varName) => {
|
|
806
|
+
const direct = varToPaths.get(varName);
|
|
807
|
+
if (direct) {
|
|
808
|
+
direct.forEach((path) => {
|
|
809
|
+
addPath(path);
|
|
810
|
+
addHint(path, varName);
|
|
811
|
+
});
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
(matchers || []).forEach(({ path, regex }) => {
|
|
815
|
+
if (regex.test(varName)) {
|
|
816
|
+
addPath(path);
|
|
817
|
+
addHint(path, varName);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
varsOrdered.forEach((varName) => matchVarName(varName));
|
|
823
|
+
|
|
824
|
+
return { paths: normalizePaths(paths), hints, debug: { vars: varsOrdered, paths } };
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function collectQuickContext(target) {
|
|
828
|
+
const computed = collectPathsFromComputedStyles(target);
|
|
829
|
+
const byComputed = computed?.paths || [];
|
|
830
|
+
const byRelations = collectPathsFromRelations(target);
|
|
831
|
+
const byQuickRules = collectQuickRulePaths(target);
|
|
832
|
+
const hints = computed?.hints || {};
|
|
833
|
+
const debug = computed?.debug || { vars: [], paths: [] };
|
|
834
|
+
|
|
835
|
+
// Prioritize quick rule paths first (selector-based), then computed/relations
|
|
836
|
+
const filtered = filterPathsByContext(target, [
|
|
837
|
+
...byQuickRules,
|
|
838
|
+
...byComputed,
|
|
839
|
+
...byRelations,
|
|
840
|
+
]);
|
|
841
|
+
if (!filtered.length) {
|
|
842
|
+
return {
|
|
843
|
+
paths: normalizePaths(DEFAULT_QUICK_PATHS),
|
|
844
|
+
hints: {},
|
|
845
|
+
debug: { vars: [], paths: [] },
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
return { paths: normalizePaths(filtered), hints, debug };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function collectDrawerPaths(quickPaths) {
|
|
852
|
+
const categories = new Set();
|
|
853
|
+
quickPaths.forEach((path) => categories.add(path.split(".")[0]));
|
|
854
|
+
const expanded = [];
|
|
855
|
+
const relationMap = collectRelationPathsByCategory();
|
|
856
|
+
categories.forEach((category) => {
|
|
857
|
+
const fields = relationMap[category] || [];
|
|
858
|
+
expanded.push(...fields);
|
|
859
|
+
});
|
|
860
|
+
return normalizePaths([...quickPaths, ...expanded]);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function buildSchemaFromPaths(paths, design, hints = {}) {
|
|
864
|
+
const schema = { type: "object", properties: {} };
|
|
865
|
+
const uiSchema = {};
|
|
866
|
+
|
|
867
|
+
const getStepFromValue = (value) => {
|
|
868
|
+
if (typeof value !== "number" || Number.isNaN(value)) return null;
|
|
869
|
+
const parts = String(value).split(".");
|
|
870
|
+
if (parts.length < 2) return 1;
|
|
871
|
+
const decimals = parts[1].length;
|
|
872
|
+
return Number(`0.${"1".padStart(decimals, "0")}`);
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
const inferRangeBounds = (path, value) => {
|
|
876
|
+
const hint = String(path || "").toLowerCase();
|
|
877
|
+
if (hint.includes("opacity")) return { min: 0, max: 1, step: 0.01 };
|
|
878
|
+
if (hint.includes("scale") || hint.includes("ratio")) {
|
|
879
|
+
return { min: 1, max: 2, step: 0.01 };
|
|
880
|
+
}
|
|
881
|
+
if (
|
|
882
|
+
hint.includes("size") ||
|
|
883
|
+
hint.includes("radius") ||
|
|
884
|
+
hint.includes("padding") ||
|
|
885
|
+
hint.includes("gap") ||
|
|
886
|
+
hint.includes("spacing") ||
|
|
887
|
+
hint.includes("width") ||
|
|
888
|
+
hint.includes("height") ||
|
|
889
|
+
hint.includes("shadow")
|
|
890
|
+
) {
|
|
891
|
+
return { min: 0, max: 64, step: 1 };
|
|
892
|
+
}
|
|
893
|
+
if (typeof value === "number") {
|
|
894
|
+
if (value >= 0 && value <= 1) return { min: 0, max: 1, step: 0.01 };
|
|
895
|
+
const magnitude = Math.max(1, Math.abs(value));
|
|
896
|
+
const upper = Math.max(10, Math.ceil(magnitude * 4));
|
|
897
|
+
return { min: 0, max: upper, step: getStepFromValue(value) || 1 };
|
|
898
|
+
}
|
|
899
|
+
return { min: 0, max: 100, step: 1 };
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
const isColorValue = (value, path) => {
|
|
903
|
+
if (String(path || "").toLowerCase().startsWith("colors.")) return true;
|
|
904
|
+
if (typeof value !== "string") return false;
|
|
905
|
+
return /^#([0-9a-f]{3,8})$/i.test(value) || /^rgba?\(/i.test(value) || /^hsla?\(/i.test(value);
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
paths.forEach((path) => {
|
|
909
|
+
const segments = path.split(".");
|
|
910
|
+
const [category, ...rest] = segments;
|
|
911
|
+
if (!category || !rest.length) return;
|
|
912
|
+
|
|
913
|
+
let parent = schema.properties[category];
|
|
914
|
+
if (!parent) {
|
|
915
|
+
parent = { type: "object", title: titleize(category), properties: {} };
|
|
916
|
+
schema.properties[category] = parent;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
let current = parent;
|
|
920
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
921
|
+
const segment = rest[i];
|
|
922
|
+
if (i === rest.length - 1) {
|
|
923
|
+
const value = getValueAtPath(design, [category, ...rest]);
|
|
924
|
+
const hintValue = hints[path];
|
|
925
|
+
const enumOptions = getEnumOptions(path);
|
|
926
|
+
const normalizedValue = normalizeEnumValue(path, value);
|
|
927
|
+
const normalizedHint = normalizeEnumValue(path, hintValue);
|
|
928
|
+
const inferredType = Array.isArray(value)
|
|
929
|
+
? "array"
|
|
930
|
+
: value === null
|
|
931
|
+
? "string"
|
|
932
|
+
: typeof value;
|
|
933
|
+
const schemaType = enumOptions?.length
|
|
934
|
+
? "string"
|
|
935
|
+
: inferredType === "number" || inferredType === "boolean"
|
|
936
|
+
? inferredType
|
|
937
|
+
: "string";
|
|
938
|
+
current.properties[segment] = {
|
|
939
|
+
type: schemaType,
|
|
940
|
+
title: titleize(segment),
|
|
941
|
+
...(enumOptions?.length
|
|
942
|
+
? {
|
|
943
|
+
oneOf: enumOptions.map((option) => ({
|
|
944
|
+
const: option,
|
|
945
|
+
title: titleize(option),
|
|
946
|
+
})),
|
|
947
|
+
}
|
|
948
|
+
: {}),
|
|
949
|
+
examples:
|
|
950
|
+
normalizedValue !== undefined && normalizedValue !== null
|
|
951
|
+
? [normalizedValue]
|
|
952
|
+
: normalizedHint !== undefined
|
|
953
|
+
? [normalizedHint]
|
|
954
|
+
: undefined,
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
const pointer = `/${[category, ...rest].join("/")}`;
|
|
958
|
+
const uiEntry = {};
|
|
959
|
+
|
|
960
|
+
if (enumOptions?.length) {
|
|
961
|
+
uiEntry["ui:widget"] = "select";
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Check for opacity/numeric fields BEFORE color check
|
|
965
|
+
const pathLower = String(path || "").toLowerCase();
|
|
966
|
+
const isOpacityField = pathLower.includes("opacity");
|
|
967
|
+
|
|
968
|
+
if (isOpacityField || (schemaType === "number" && !isColorValue(value, path))) {
|
|
969
|
+
const bounds = inferRangeBounds(path, value);
|
|
970
|
+
uiEntry["ui:widget"] = "input-range";
|
|
971
|
+
uiEntry["ui:min"] = bounds.min;
|
|
972
|
+
uiEntry["ui:max"] = bounds.max;
|
|
973
|
+
uiEntry["ui:step"] = bounds.step;
|
|
974
|
+
} else if (isColorValue(value, path)) {
|
|
975
|
+
uiEntry["ui:widget"] = "input-color";
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
const isTextOrNumberInput =
|
|
979
|
+
(schemaType === "string" || schemaType === "number") &&
|
|
980
|
+
!uiEntry["ui:widget"];
|
|
981
|
+
if (isTextOrNumberInput) {
|
|
982
|
+
uiEntry["ui:icon"] = CATEGORY_ICONS[category] || "sparkle";
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
uiSchema[pointer] = uiEntry;
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
if (!current.properties[segment]) {
|
|
990
|
+
current.properties[segment] = { type: "object", properties: {} };
|
|
991
|
+
}
|
|
992
|
+
current = current.properties[segment];
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
return { schema, uiSchema };
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
async function getGeneratorClass() {
|
|
1000
|
+
if (!PDS?.getGenerator) return null;
|
|
1001
|
+
try {
|
|
1002
|
+
return await PDS.getGenerator();
|
|
1003
|
+
} catch (e) {
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
async function applyDesignPatch(patch) {
|
|
1009
|
+
if (!patch || typeof patch !== "object") return;
|
|
1010
|
+
const Generator = await getGeneratorClass();
|
|
1011
|
+
const generator = Generator?.instance;
|
|
1012
|
+
if (!generator || !generator.options) return;
|
|
1013
|
+
|
|
1014
|
+
const presetKeyMatches = (key, compareTo) => {
|
|
1015
|
+
if (!key || !compareTo) return false;
|
|
1016
|
+
return slugifyPreset(key) === slugifyPreset(compareTo);
|
|
1017
|
+
};
|
|
1018
|
+
|
|
1019
|
+
const slugifyPreset = (value) =>
|
|
1020
|
+
String(value || "")
|
|
1021
|
+
.toLowerCase()
|
|
1022
|
+
.replace(/&/g, " and ")
|
|
1023
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
1024
|
+
.replace(/^-+|-+$/g, "");
|
|
1025
|
+
|
|
1026
|
+
const resolvePresetBase = (presetId) => {
|
|
1027
|
+
const presets = PDS?.presets || {};
|
|
1028
|
+
if (!presetId) return { id: null, preset: null };
|
|
1029
|
+
if (presets[presetId]) {
|
|
1030
|
+
return { id: presetId, preset: presets[presetId] };
|
|
1031
|
+
}
|
|
1032
|
+
const presetKeys = Object.keys(presets || {});
|
|
1033
|
+
const matchedKey = presetKeys.find((key) => presetKeyMatches(key, presetId));
|
|
1034
|
+
if (matchedKey) {
|
|
1035
|
+
return { id: matchedKey, preset: presets[matchedKey] };
|
|
1036
|
+
}
|
|
1037
|
+
const found = Object.values(presets).find((preset) => {
|
|
1038
|
+
const name = preset?.name || preset?.id || "";
|
|
1039
|
+
return presetKeyMatches(name, presetId);
|
|
1040
|
+
});
|
|
1041
|
+
if (found) {
|
|
1042
|
+
const foundId = found.id || found.name || presetId;
|
|
1043
|
+
return { id: foundId, preset: found };
|
|
1044
|
+
}
|
|
1045
|
+
return { id: presetId, preset: null };
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const currentOptions = generator.options;
|
|
1049
|
+
let storedConfig = null;
|
|
1050
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
1051
|
+
try {
|
|
1052
|
+
const raw = window.localStorage.getItem("pure-ds-config");
|
|
1053
|
+
if (raw) {
|
|
1054
|
+
const parsed = JSON.parse(raw);
|
|
1055
|
+
if (parsed && ("preset" in parsed || "design" in parsed)) {
|
|
1056
|
+
storedConfig = parsed;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
} catch (e) {
|
|
1060
|
+
storedConfig = null;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const storedPreset = storedConfig?.preset;
|
|
1065
|
+
const hasStoredPreset = Boolean(storedPreset);
|
|
1066
|
+
const storedOverrides =
|
|
1067
|
+
storedConfig && storedConfig.design && typeof storedConfig.design === "object"
|
|
1068
|
+
? storedConfig.design
|
|
1069
|
+
: {};
|
|
1070
|
+
let presetId = storedPreset || currentOptions.preset || PDS?.currentConfig?.preset || null;
|
|
1071
|
+
const inferredPreset = currentOptions.design?.id || currentOptions.design?.name || null;
|
|
1072
|
+
if (!presetId && inferredPreset && !hasStoredPreset) {
|
|
1073
|
+
const inferredMatch = resolvePresetBase(inferredPreset);
|
|
1074
|
+
if (inferredMatch?.preset) presetId = inferredMatch.id;
|
|
1075
|
+
}
|
|
1076
|
+
if (String(presetId || "").toLowerCase() === "default" && inferredPreset && !hasStoredPreset) {
|
|
1077
|
+
const inferredMatch = resolvePresetBase(inferredPreset);
|
|
1078
|
+
if (inferredMatch?.preset) presetId = inferredMatch.id;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const resolvedPreset = resolvePresetBase(presetId);
|
|
1082
|
+
const resolvedPresetId = resolvedPreset.id || presetId || null;
|
|
1083
|
+
const presetBase = resolvedPreset.preset || null;
|
|
1084
|
+
|
|
1085
|
+
const baseDesign = presetBase
|
|
1086
|
+
? deepMerge(shallowClone(presetBase), storedOverrides)
|
|
1087
|
+
: shallowClone(currentOptions.design || {});
|
|
1088
|
+
const nextDesign = deepMerge(shallowClone(baseDesign), patch);
|
|
1089
|
+
const nextOptions = { ...currentOptions, design: nextDesign };
|
|
1090
|
+
if (resolvedPresetId) nextOptions.preset = resolvedPresetId;
|
|
1091
|
+
|
|
1092
|
+
const nextGenerator = new Generator(nextOptions);
|
|
1093
|
+
if (PDS?.applyStyles) {
|
|
1094
|
+
await PDS.applyStyles(nextGenerator);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (PDS) {
|
|
1098
|
+
try {
|
|
1099
|
+
PDS.currentConfig = Object.freeze({
|
|
1100
|
+
...(PDS.currentConfig || {}),
|
|
1101
|
+
design: structuredClone(nextDesign),
|
|
1102
|
+
preset: resolvedPresetId || PDS.currentConfig?.preset,
|
|
1103
|
+
});
|
|
1104
|
+
} catch (e) {
|
|
1105
|
+
PDS.currentConfig = {
|
|
1106
|
+
...(PDS.currentConfig || {}),
|
|
1107
|
+
design: nextDesign,
|
|
1108
|
+
preset: resolvedPresetId || PDS.currentConfig?.preset,
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
try {
|
|
1113
|
+
const event = new CustomEvent("design-updated", {
|
|
1114
|
+
detail: { config: nextDesign },
|
|
1115
|
+
});
|
|
1116
|
+
PDS.dispatchEvent(event);
|
|
1117
|
+
} catch (e) {}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
1121
|
+
try {
|
|
1122
|
+
const nextStored = {
|
|
1123
|
+
preset: resolvedPresetId || null,
|
|
1124
|
+
design: shallowClone(nextDesign),
|
|
1125
|
+
};
|
|
1126
|
+
window.localStorage.setItem("pure-ds-config", JSON.stringify(nextStored));
|
|
1127
|
+
} catch (e) {}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function getStoredConfig() {
|
|
1132
|
+
if (typeof window === "undefined" || !window.localStorage) return null;
|
|
1133
|
+
try {
|
|
1134
|
+
const raw = window.localStorage.getItem("pure-ds-config");
|
|
1135
|
+
if (!raw) return null;
|
|
1136
|
+
const parsed = JSON.parse(raw);
|
|
1137
|
+
if (parsed && ("preset" in parsed || "design" in parsed)) return parsed;
|
|
1138
|
+
} catch (e) {
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
function setStoredConfig(nextConfig) {
|
|
1145
|
+
if (typeof window === "undefined" || !window.localStorage) return;
|
|
1146
|
+
try {
|
|
1147
|
+
window.localStorage.setItem("pure-ds-config", JSON.stringify(nextConfig));
|
|
1148
|
+
} catch (e) {}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function getPresetOptions() {
|
|
1152
|
+
const presets = PDS?.presets || {};
|
|
1153
|
+
return Object.values(presets)
|
|
1154
|
+
.map((preset) => ({
|
|
1155
|
+
id: preset?.id || preset?.name,
|
|
1156
|
+
name: preset?.name || preset?.id || "Unnamed",
|
|
1157
|
+
}))
|
|
1158
|
+
.filter((preset) => preset.id)
|
|
1159
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function getActivePresetId() {
|
|
1163
|
+
const stored = getStoredConfig();
|
|
1164
|
+
return stored?.preset || PDS?.currentConfig?.preset || PDS?.currentPreset || null;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
async function applyPresetSelection(presetId) {
|
|
1168
|
+
if (!presetId) return;
|
|
1169
|
+
setStoredConfig({
|
|
1170
|
+
preset: presetId,
|
|
1171
|
+
design: {},
|
|
1172
|
+
});
|
|
1173
|
+
await applyDesignPatch({});
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
function setFormSchemas(form, schema, uiSchema, design) {
|
|
1177
|
+
form.jsonSchema = schema;
|
|
1178
|
+
form.uiSchema = uiSchema;
|
|
1179
|
+
form.values = shallowClone(design);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
async function buildForm(paths, design, onSubmit, onUndo, hints = {}) {
|
|
1183
|
+
const { schema, uiSchema } = buildSchemaFromPaths(paths, design, hints);
|
|
1184
|
+
const form = document.createElement("pds-form");
|
|
1185
|
+
form.setAttribute("hide-actions", "");
|
|
1186
|
+
form.options = {
|
|
1187
|
+
layouts: {
|
|
1188
|
+
arrays: "compact",
|
|
1189
|
+
},
|
|
1190
|
+
enhancements: {
|
|
1191
|
+
rangeOutput: true,
|
|
1192
|
+
},
|
|
1193
|
+
};
|
|
1194
|
+
form.addEventListener("pw:submit", onSubmit);
|
|
1195
|
+
const values = shallowClone(design || {});
|
|
1196
|
+
Object.keys(ENUM_FIELD_OPTIONS).forEach((path) => {
|
|
1197
|
+
const normalized = normalizeEnumValue(path, getValueAtPath(values, path.split(".")));
|
|
1198
|
+
if (normalized !== undefined) {
|
|
1199
|
+
setValueAtPath(values, path.split("."), normalized);
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
Object.entries(hints || {}).forEach(([path, hintValue]) => {
|
|
1203
|
+
const segments = path.split(".");
|
|
1204
|
+
const currentValue = getValueAtPath(values, segments);
|
|
1205
|
+
if (currentValue === undefined || currentValue === null) {
|
|
1206
|
+
setValueAtPath(values, segments, hintValue);
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
setFormSchemas(form, schema, uiSchema, values);
|
|
1210
|
+
|
|
1211
|
+
if (!customElements.get("pds-form")) {
|
|
1212
|
+
customElements.whenDefined("pds-form").then(() => {
|
|
1213
|
+
setFormSchemas(form, schema, uiSchema, values);
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// Apply button (will trigger form submit programmatically)
|
|
1218
|
+
const applyBtn = document.createElement("button");
|
|
1219
|
+
applyBtn.className = "btn-primary btn-sm";
|
|
1220
|
+
applyBtn.type = "button";
|
|
1221
|
+
applyBtn.textContent = "Apply";
|
|
1222
|
+
applyBtn.addEventListener("click", async () => {
|
|
1223
|
+
// Manually trigger pw:submit event for pds-form
|
|
1224
|
+
if (typeof form.getValuesFlat === "function") {
|
|
1225
|
+
// Wait for form to be ready if it's still loading
|
|
1226
|
+
if (!customElements.get("pds-form")) {
|
|
1227
|
+
await customElements.whenDefined("pds-form");
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const flatValues = form.getValuesFlat();
|
|
1231
|
+
const event = new CustomEvent("pw:submit", {
|
|
1232
|
+
detail: {
|
|
1233
|
+
json: flatValues,
|
|
1234
|
+
formData: new FormData(),
|
|
1235
|
+
valid: true,
|
|
1236
|
+
issues: []
|
|
1237
|
+
},
|
|
1238
|
+
bubbles: true,
|
|
1239
|
+
cancelable: true
|
|
1240
|
+
});
|
|
1241
|
+
form.dispatchEvent(event);
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
// Undo button
|
|
1246
|
+
const undoBtn = document.createElement("button");
|
|
1247
|
+
undoBtn.className = "btn-secondary btn-sm";
|
|
1248
|
+
undoBtn.type = "button";
|
|
1249
|
+
undoBtn.textContent = "Undo";
|
|
1250
|
+
undoBtn.addEventListener("click", onUndo);
|
|
1251
|
+
|
|
1252
|
+
return { form, applyBtn, undoBtn };
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
class PdsLiveEdit extends HTMLElement {
|
|
1256
|
+
constructor() {
|
|
1257
|
+
super();
|
|
1258
|
+
this._boundMouseOver = this._handleMouseOver.bind(this);
|
|
1259
|
+
this._boundMouseOut = this._handleMouseOut.bind(this);
|
|
1260
|
+
this._boundMouseMove = this._handleMouseMove.bind(this);
|
|
1261
|
+
this._boundReposition = this._repositionDropdown.bind(this);
|
|
1262
|
+
this._activeTarget = null;
|
|
1263
|
+
this._activeDropdown = null;
|
|
1264
|
+
this._holdOpen = false;
|
|
1265
|
+
this._closeTimer = null;
|
|
1266
|
+
this._drawer = null;
|
|
1267
|
+
this._selectors = null;
|
|
1268
|
+
this._lastPointer = null;
|
|
1269
|
+
this._boundDocKeydown = this._handleDocumentKeydown.bind(this);
|
|
1270
|
+
this._connected = false;
|
|
1271
|
+
this._undoStack = [];
|
|
1272
|
+
this._dropdownMenuOpen = false;
|
|
1273
|
+
this._dropdownObserver = null;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
connectedCallback() {
|
|
1277
|
+
if (this._connected) return;
|
|
1278
|
+
if (PdsLiveEdit._activeInstance && PdsLiveEdit._activeInstance !== this) {
|
|
1279
|
+
PdsLiveEdit._activeInstance._teardown();
|
|
1280
|
+
}
|
|
1281
|
+
PdsLiveEdit._activeInstance = this;
|
|
1282
|
+
this._connected = true;
|
|
1283
|
+
if (!isHoverCapable()) return;
|
|
1284
|
+
|
|
1285
|
+
ensureStyles();
|
|
1286
|
+
this._selectors = collectSelectors();
|
|
1287
|
+
document.addEventListener("mouseover", this._boundMouseOver, true);
|
|
1288
|
+
document.addEventListener("mouseout", this._boundMouseOut, true);
|
|
1289
|
+
document.addEventListener("mousemove", this._boundMouseMove, true);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
disconnectedCallback() {
|
|
1293
|
+
this._teardown();
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
_teardown() {
|
|
1297
|
+
if (this._connected) {
|
|
1298
|
+
document.removeEventListener("mouseover", this._boundMouseOver, true);
|
|
1299
|
+
document.removeEventListener("mouseout", this._boundMouseOut, true);
|
|
1300
|
+
document.removeEventListener("mousemove", this._boundMouseMove, true);
|
|
1301
|
+
}
|
|
1302
|
+
this._removeRepositionListeners();
|
|
1303
|
+
this._clearCloseTimer();
|
|
1304
|
+
this._removeActiveUI();
|
|
1305
|
+
this._connected = false;
|
|
1306
|
+
if (PdsLiveEdit._activeInstance === this) {
|
|
1307
|
+
PdsLiveEdit._activeInstance = null;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
_handleMouseOver(event) {
|
|
1312
|
+
if (!event?.target || !(event.target instanceof Element)) return;
|
|
1313
|
+
|
|
1314
|
+
// Check if we're hovering over the dropdown (including Shadow DOM elements)
|
|
1315
|
+
if (this._activeDropdown) {
|
|
1316
|
+
const path = event.composedPath ? event.composedPath() : [event.target];
|
|
1317
|
+
const isOverDropdown = path.some(node => node === this._activeDropdown);
|
|
1318
|
+
if (isOverDropdown) {
|
|
1319
|
+
this._clearCloseTimer();
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
const target = this._findEditableTarget(event.target);
|
|
1325
|
+
|
|
1326
|
+
// If hovering over the same active target, just clear timer
|
|
1327
|
+
if (target && target === this._activeTarget) {
|
|
1328
|
+
this._clearCloseTimer();
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// If hovering over a new target, show its editor
|
|
1333
|
+
if (target && target !== this._activeTarget) {
|
|
1334
|
+
this._removeActiveUI();
|
|
1335
|
+
this._showForTarget(target);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
_handleMouseOut(event) {
|
|
1340
|
+
if (!this._activeTarget) return;
|
|
1341
|
+
|
|
1342
|
+
// Schedule a delayed close - the safe zone logic will determine if we actually close
|
|
1343
|
+
this._scheduleClose();
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
_findEditableTarget(node) {
|
|
1347
|
+
const tag = node.tagName?.toLowerCase?.();
|
|
1348
|
+
if (tag && ["html", "head", "meta", "link", "style", "script", "title"].includes(tag)) {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
if (!this._selectors?.selector) return null;
|
|
1352
|
+
if (node.closest(EDITOR_TAG)) return null;
|
|
1353
|
+
if (node.closest(`.${DROPDOWN_CLASS}`)) return null;
|
|
1354
|
+
if (node.closest("pds-drawer")) return null;
|
|
1355
|
+
|
|
1356
|
+
try {
|
|
1357
|
+
return node.closest(this._selectors.selector);
|
|
1358
|
+
} catch (e) {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
_showForTarget(target) {
|
|
1364
|
+
const quickContext = collectQuickContext(target);
|
|
1365
|
+
const quickPaths = quickContext.paths;
|
|
1366
|
+
if (!quickPaths.length) return;
|
|
1367
|
+
|
|
1368
|
+
this._holdOpen = true;
|
|
1369
|
+
|
|
1370
|
+
target.setAttribute(TARGET_ATTR, "true");
|
|
1371
|
+
const dropdown = this._buildDropdown(
|
|
1372
|
+
target,
|
|
1373
|
+
quickPaths,
|
|
1374
|
+
quickContext.hints,
|
|
1375
|
+
quickContext.debug
|
|
1376
|
+
);
|
|
1377
|
+
document.body.appendChild(dropdown);
|
|
1378
|
+
this._positionDropdown(target, dropdown);
|
|
1379
|
+
this._addRepositionListeners();
|
|
1380
|
+
this._addDocumentListeners();
|
|
1381
|
+
this._addMouseMoveListener();
|
|
1382
|
+
|
|
1383
|
+
this._activeTarget = target;
|
|
1384
|
+
this._activeDropdown = dropdown;
|
|
1385
|
+
|
|
1386
|
+
// Watch for dropdown menu opening/closing
|
|
1387
|
+
this._watchDropdownState();
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
_removeActiveUI() {
|
|
1391
|
+
this._clearCloseTimer();
|
|
1392
|
+
this._removeRepositionListeners();
|
|
1393
|
+
this._removeDocumentListeners();
|
|
1394
|
+
this._removeMouseMoveListener();
|
|
1395
|
+
this._unwatchDropdownState();
|
|
1396
|
+
if (this._activeDropdown && this._activeDropdown.parentNode) {
|
|
1397
|
+
this._activeDropdown.parentNode.removeChild(this._activeDropdown);
|
|
1398
|
+
}
|
|
1399
|
+
if (this._activeTarget) {
|
|
1400
|
+
this._activeTarget.removeAttribute(TARGET_ATTR);
|
|
1401
|
+
}
|
|
1402
|
+
this._activeTarget = null;
|
|
1403
|
+
this._activeDropdown = null;
|
|
1404
|
+
this._holdOpen = false;
|
|
1405
|
+
this._lastPointer = null;
|
|
1406
|
+
this._dropdownMenuOpen = false;
|
|
1407
|
+
|
|
1408
|
+
// Always re-enable mouseover when UI is removed
|
|
1409
|
+
this._addMouseOverListener();
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
_addDocumentListeners() {
|
|
1413
|
+
if (typeof document === "undefined") return;
|
|
1414
|
+
document.addEventListener("keydown", this._boundDocKeydown, true);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
_removeDocumentListeners() {
|
|
1418
|
+
if (typeof document === "undefined") return;
|
|
1419
|
+
document.removeEventListener("keydown", this._boundDocKeydown, true);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
_handleDocumentKeydown(event) {
|
|
1423
|
+
if (!event || event.key !== "Escape") return;
|
|
1424
|
+
if (this._activeDropdown || this._activeTarget) {
|
|
1425
|
+
event.preventDefault();
|
|
1426
|
+
this._removeActiveUI();
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
_scheduleClose() {
|
|
1431
|
+
if (typeof window === "undefined") return;
|
|
1432
|
+
this._clearCloseTimer();
|
|
1433
|
+
this._closeTimer = window.setTimeout(() => {
|
|
1434
|
+
if (this._holdOpen) return;
|
|
1435
|
+
if (this._activeDropdown && this._activeDropdown.matches(":hover")) return;
|
|
1436
|
+
if (this._activeTarget && this._activeTarget.matches(":hover")) return;
|
|
1437
|
+
if (this._isPointerWithinSafeZone()) {
|
|
1438
|
+
// Still in safe zone, check again soon
|
|
1439
|
+
this._scheduleClose();
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
this._removeActiveUI();
|
|
1443
|
+
}, 300);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
_clearCloseTimer() {
|
|
1447
|
+
if (this._closeTimer) {
|
|
1448
|
+
clearTimeout(this._closeTimer);
|
|
1449
|
+
this._closeTimer = null;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
_addRepositionListeners() {
|
|
1454
|
+
if (typeof window === "undefined") return;
|
|
1455
|
+
window.addEventListener("scroll", this._boundReposition, true);
|
|
1456
|
+
window.addEventListener("resize", this._boundReposition);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
_removeRepositionListeners() {
|
|
1460
|
+
if (typeof window === "undefined") return;
|
|
1461
|
+
window.removeEventListener("scroll", this._boundReposition, true);
|
|
1462
|
+
window.removeEventListener("resize", this._boundReposition);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
_repositionDropdown() {
|
|
1466
|
+
if (!this._activeTarget || !this._activeDropdown) return;
|
|
1467
|
+
if (!document.contains(this._activeTarget)) {
|
|
1468
|
+
this._removeActiveUI();
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1471
|
+
this._positionDropdown(this._activeTarget, this._activeDropdown);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
_positionDropdown(target, dropdown) {
|
|
1475
|
+
if (!target || !dropdown) return;
|
|
1476
|
+
const rect = target.getBoundingClientRect();
|
|
1477
|
+
const spacing = getSpacingOffset();
|
|
1478
|
+
const width = Math.max(dropdown.offsetWidth || 0, 160);
|
|
1479
|
+
const height = Math.max(dropdown.offsetHeight || 0, 120);
|
|
1480
|
+
const spaceRight = Math.max(0, window.innerWidth - rect.right);
|
|
1481
|
+
const spaceLeft = Math.max(0, rect.left);
|
|
1482
|
+
const spaceBelow = Math.max(0, window.innerHeight - rect.bottom);
|
|
1483
|
+
const spaceAbove = Math.max(0, rect.top);
|
|
1484
|
+
|
|
1485
|
+
const alignRight = spaceRight >= width || spaceRight >= spaceLeft;
|
|
1486
|
+
const alignBottom = spaceBelow >= height || spaceBelow >= spaceAbove;
|
|
1487
|
+
|
|
1488
|
+
const rightOffset = Math.max(0, window.innerWidth - rect.right);
|
|
1489
|
+
const bottomOffset = Math.max(0, window.innerHeight - rect.bottom);
|
|
1490
|
+
|
|
1491
|
+
dropdown.style.setProperty(
|
|
1492
|
+
"--pds-live-edit-left",
|
|
1493
|
+
alignRight ? `${rect.left + spacing}px` : "auto"
|
|
1494
|
+
);
|
|
1495
|
+
dropdown.style.setProperty(
|
|
1496
|
+
"--pds-live-edit-right",
|
|
1497
|
+
alignRight ? "auto" : `${rightOffset + spacing}px`
|
|
1498
|
+
);
|
|
1499
|
+
dropdown.style.setProperty(
|
|
1500
|
+
"--pds-live-edit-top",
|
|
1501
|
+
alignBottom ? `${rect.top + spacing}px` : "auto"
|
|
1502
|
+
);
|
|
1503
|
+
dropdown.style.setProperty(
|
|
1504
|
+
"--pds-live-edit-bottom",
|
|
1505
|
+
alignBottom ? "auto" : `${bottomOffset + spacing}px`
|
|
1506
|
+
);
|
|
1507
|
+
|
|
1508
|
+
const adjusted = dropdown.getBoundingClientRect();
|
|
1509
|
+
let shiftX = 0;
|
|
1510
|
+
let shiftY = 0;
|
|
1511
|
+
if (adjusted.left < DROPDOWN_VIEWPORT_PADDING) {
|
|
1512
|
+
shiftX = DROPDOWN_VIEWPORT_PADDING - adjusted.left;
|
|
1513
|
+
} else if (adjusted.right > window.innerWidth - DROPDOWN_VIEWPORT_PADDING) {
|
|
1514
|
+
shiftX = window.innerWidth - DROPDOWN_VIEWPORT_PADDING - adjusted.right;
|
|
1515
|
+
}
|
|
1516
|
+
if (adjusted.top < DROPDOWN_VIEWPORT_PADDING) {
|
|
1517
|
+
shiftY = DROPDOWN_VIEWPORT_PADDING - adjusted.top;
|
|
1518
|
+
} else if (adjusted.bottom > window.innerHeight - DROPDOWN_VIEWPORT_PADDING) {
|
|
1519
|
+
shiftY = window.innerHeight - DROPDOWN_VIEWPORT_PADDING - adjusted.bottom;
|
|
1520
|
+
}
|
|
1521
|
+
if (shiftX || shiftY) {
|
|
1522
|
+
const currentLeft = parseFloat(dropdown.style.getPropertyValue("--pds-live-edit-left"));
|
|
1523
|
+
const currentTop = parseFloat(dropdown.style.getPropertyValue("--pds-live-edit-top"));
|
|
1524
|
+
const currentRight = parseFloat(dropdown.style.getPropertyValue("--pds-live-edit-right"));
|
|
1525
|
+
const currentBottom = parseFloat(dropdown.style.getPropertyValue("--pds-live-edit-bottom"));
|
|
1526
|
+
if (!Number.isNaN(currentLeft)) {
|
|
1527
|
+
dropdown.style.setProperty("--pds-live-edit-left", `${currentLeft + shiftX}px`);
|
|
1528
|
+
} else if (!Number.isNaN(currentRight)) {
|
|
1529
|
+
dropdown.style.setProperty("--pds-live-edit-right", `${currentRight - shiftX}px`);
|
|
1530
|
+
}
|
|
1531
|
+
if (!Number.isNaN(currentTop)) {
|
|
1532
|
+
dropdown.style.setProperty("--pds-live-edit-top", `${currentTop + shiftY}px`);
|
|
1533
|
+
} else if (!Number.isNaN(currentBottom)) {
|
|
1534
|
+
dropdown.style.setProperty("--pds-live-edit-bottom", `${currentBottom - shiftY}px`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
_handleMouseMove(event) {
|
|
1540
|
+
if (!event) return;
|
|
1541
|
+
this._lastPointer = { x: event.clientX, y: event.clientY };
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
_addMouseMoveListener() {
|
|
1545
|
+
if (typeof document === "undefined") return;
|
|
1546
|
+
document.addEventListener("mousemove", this._boundMouseMove, true);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
_removeMouseMoveListener() {
|
|
1550
|
+
if (typeof document === "undefined") return;
|
|
1551
|
+
document.removeEventListener("mousemove", this._boundMouseMove, true);
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
_addMouseOverListener() {
|
|
1555
|
+
if (typeof document === "undefined") return;
|
|
1556
|
+
document.addEventListener("mouseover", this._boundMouseOver, true);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
_removeMouseOverListener() {
|
|
1560
|
+
if (typeof document === "undefined") return;
|
|
1561
|
+
document.removeEventListener("mouseover", this._boundMouseOver, true);
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
_watchDropdownState() {
|
|
1565
|
+
if (!this._activeDropdown) return;
|
|
1566
|
+
|
|
1567
|
+
const menu = this._activeDropdown.querySelector("menu");
|
|
1568
|
+
if (!menu) return;
|
|
1569
|
+
|
|
1570
|
+
// Create a MutationObserver to watch for aria-hidden changes
|
|
1571
|
+
this._dropdownObserver = new MutationObserver((mutations) => {
|
|
1572
|
+
mutations.forEach((mutation) => {
|
|
1573
|
+
if (mutation.attributeName === "aria-hidden") {
|
|
1574
|
+
const isOpen = menu.getAttribute("aria-hidden") === "false";
|
|
1575
|
+
|
|
1576
|
+
if (isOpen && !this._dropdownMenuOpen) {
|
|
1577
|
+
// Dropdown just opened - pause mouseover
|
|
1578
|
+
this._dropdownMenuOpen = true;
|
|
1579
|
+
this._removeMouseOverListener();
|
|
1580
|
+
} else if (!isOpen && this._dropdownMenuOpen) {
|
|
1581
|
+
// Dropdown just closed - resume mouseover
|
|
1582
|
+
this._dropdownMenuOpen = false;
|
|
1583
|
+
this._addMouseOverListener();
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1589
|
+
this._dropdownObserver.observe(menu, {
|
|
1590
|
+
attributes: true,
|
|
1591
|
+
attributeFilter: ["aria-hidden"]
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
_unwatchDropdownState() {
|
|
1596
|
+
if (this._dropdownObserver) {
|
|
1597
|
+
this._dropdownObserver.disconnect();
|
|
1598
|
+
this._dropdownObserver = null;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
_watchDropdownState() {
|
|
1603
|
+
if (!this._activeDropdown) return;
|
|
1604
|
+
|
|
1605
|
+
const menu = this._activeDropdown.querySelector("menu");
|
|
1606
|
+
if (!menu) return;
|
|
1607
|
+
|
|
1608
|
+
// Create a MutationObserver to watch for aria-hidden changes
|
|
1609
|
+
this._dropdownObserver = new MutationObserver((mutations) => {
|
|
1610
|
+
mutations.forEach((mutation) => {
|
|
1611
|
+
if (mutation.attributeName === "aria-hidden") {
|
|
1612
|
+
const isOpen = menu.getAttribute("aria-hidden") === "false";
|
|
1613
|
+
|
|
1614
|
+
if (isOpen && !this._dropdownMenuOpen) {
|
|
1615
|
+
// Dropdown just opened - pause mouseover
|
|
1616
|
+
this._dropdownMenuOpen = true;
|
|
1617
|
+
this._removeMouseOverListener();
|
|
1618
|
+
} else if (!isOpen && this._dropdownMenuOpen) {
|
|
1619
|
+
// Dropdown just closed - resume mouseover
|
|
1620
|
+
this._dropdownMenuOpen = false;
|
|
1621
|
+
this._addMouseOverListener();
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
this._dropdownObserver.observe(menu, {
|
|
1628
|
+
attributes: true,
|
|
1629
|
+
attributeFilter: ["aria-hidden"]
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
_unwatchDropdownState() {
|
|
1634
|
+
if (this._dropdownObserver) {
|
|
1635
|
+
this._dropdownObserver.disconnect();
|
|
1636
|
+
this._dropdownObserver = null;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
_isPointerWithinSafeZone() {
|
|
1641
|
+
if (!this._lastPointer || !this._activeTarget || !this._activeDropdown) return false;
|
|
1642
|
+
const targetRect = this._activeTarget.getBoundingClientRect();
|
|
1643
|
+
const dropdownRect = this._activeDropdown.getBoundingClientRect();
|
|
1644
|
+
const padding = 12;
|
|
1645
|
+
const left = Math.min(targetRect.left, dropdownRect.left) - padding;
|
|
1646
|
+
const right = Math.max(targetRect.right, dropdownRect.right) + padding;
|
|
1647
|
+
const top = Math.min(targetRect.top, dropdownRect.top) - padding;
|
|
1648
|
+
const bottom = Math.max(targetRect.bottom, dropdownRect.bottom) + padding;
|
|
1649
|
+
const { x, y } = this._lastPointer;
|
|
1650
|
+
return x >= left && x <= right && y >= top && y <= bottom;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
_buildDropdown(target, quickPaths, hints, debug) {
|
|
1654
|
+
const nav = document.createElement("nav");
|
|
1655
|
+
nav.className = DROPDOWN_CLASS;
|
|
1656
|
+
nav.setAttribute("data-dropdown", "");
|
|
1657
|
+
nav.setAttribute("data-direction", "auto");
|
|
1658
|
+
nav.setAttribute("data-mode", "auto");
|
|
1659
|
+
|
|
1660
|
+
const button = document.createElement("button");
|
|
1661
|
+
button.className = `context-edit btn-primary btn-xs icon-only ${MARKER_CLASS}`;
|
|
1662
|
+
button.setAttribute("type", "button");
|
|
1663
|
+
button.setAttribute("data-direction", "auto");
|
|
1664
|
+
button.setAttribute("aria-label", "Edit design settings");
|
|
1665
|
+
|
|
1666
|
+
const icon = document.createElement("pds-icon");
|
|
1667
|
+
icon.setAttribute("icon", "pencil");
|
|
1668
|
+
icon.setAttribute("size", "sm");
|
|
1669
|
+
button.appendChild(icon);
|
|
1670
|
+
|
|
1671
|
+
const menu = document.createElement("menu");
|
|
1672
|
+
const quickItem = document.createElement("li");
|
|
1673
|
+
quickItem.className = "pds-live-editor-menu";
|
|
1674
|
+
|
|
1675
|
+
const header = document.createElement("div");
|
|
1676
|
+
header.className = "pds-live-editor-header";
|
|
1677
|
+
|
|
1678
|
+
const title = document.createElement("span");
|
|
1679
|
+
title.className = "pds-live-editor-title";
|
|
1680
|
+
title.textContent = "Quick edit";
|
|
1681
|
+
header.appendChild(title);
|
|
1682
|
+
|
|
1683
|
+
quickItem.appendChild(header);
|
|
1684
|
+
|
|
1685
|
+
const design = shallowClone(PDS?.currentConfig?.design || {});
|
|
1686
|
+
const formContainer = document.createElement("div");
|
|
1687
|
+
formContainer.className = "pds-live-editor-form-container";
|
|
1688
|
+
quickItem.appendChild(formContainer);
|
|
1689
|
+
|
|
1690
|
+
// Create footer with Apply/Undo/Gear buttons
|
|
1691
|
+
const footer = document.createElement("div");
|
|
1692
|
+
footer.className = "pds-live-editor-footer";
|
|
1693
|
+
quickItem.appendChild(footer);
|
|
1694
|
+
|
|
1695
|
+
menu.appendChild(quickItem);
|
|
1696
|
+
|
|
1697
|
+
nav.appendChild(button);
|
|
1698
|
+
nav.appendChild(menu);
|
|
1699
|
+
|
|
1700
|
+
const limitedPaths = quickPaths.slice(0, QUICK_EDIT_LIMIT);
|
|
1701
|
+
this._renderQuickForm(formContainer, footer, limitedPaths, design, hints, target, quickPaths);
|
|
1702
|
+
|
|
1703
|
+
// Log debug info to console instead of rendering
|
|
1704
|
+
if (debug && (debug.vars?.length || debug.paths?.length)) {
|
|
1705
|
+
const debugVars = (debug.vars || []).slice(0, 8).join(", ");
|
|
1706
|
+
const debugPaths = (debug.paths || []).slice(0, 8).join(", ");
|
|
1707
|
+
console.log(`[PDS Live Edit] vars: ${debugVars}`);
|
|
1708
|
+
console.log(`[PDS Live Edit] paths: ${debugPaths}`);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
return nav;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
async _renderQuickForm(container, footer, paths, design, hints, target, quickPaths) {
|
|
1715
|
+
container.replaceChildren();
|
|
1716
|
+
footer.replaceChildren();
|
|
1717
|
+
|
|
1718
|
+
const { form, applyBtn, undoBtn } = await buildForm(
|
|
1719
|
+
paths,
|
|
1720
|
+
design,
|
|
1721
|
+
(event) => this._handleFormSubmit(event, form),
|
|
1722
|
+
() => this._handleUndo(),
|
|
1723
|
+
hints
|
|
1724
|
+
);
|
|
1725
|
+
|
|
1726
|
+
// Store reference to undo button for enabling/disabling
|
|
1727
|
+
form._undoBtn = undoBtn;
|
|
1728
|
+
|
|
1729
|
+
// Disable undo initially if no history
|
|
1730
|
+
undoBtn.disabled = this._undoStack.length === 0;
|
|
1731
|
+
|
|
1732
|
+
// Add form to container
|
|
1733
|
+
container.appendChild(form);
|
|
1734
|
+
|
|
1735
|
+
// Create gear button for footer
|
|
1736
|
+
const gearBtn = document.createElement("button");
|
|
1737
|
+
gearBtn.className = "btn-outline btn-sm icon-only";
|
|
1738
|
+
gearBtn.type = "button";
|
|
1739
|
+
gearBtn.setAttribute("aria-label", "More settings");
|
|
1740
|
+
const gearIcon = document.createElement("pds-icon");
|
|
1741
|
+
gearIcon.setAttribute("icon", "caret-right");
|
|
1742
|
+
gearIcon.setAttribute("size", "sm");
|
|
1743
|
+
gearBtn.appendChild(gearIcon);
|
|
1744
|
+
gearBtn.addEventListener("click", (event) => {
|
|
1745
|
+
event.preventDefault();
|
|
1746
|
+
event.stopPropagation();
|
|
1747
|
+
this._openDrawer(target, quickPaths);
|
|
1748
|
+
this._removeActiveUI();
|
|
1749
|
+
});
|
|
1750
|
+
|
|
1751
|
+
// Add buttons to footer
|
|
1752
|
+
footer.appendChild(applyBtn);
|
|
1753
|
+
footer.appendChild(undoBtn);
|
|
1754
|
+
footer.appendChild(gearBtn);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
async _openDrawer(target, quickPaths) {
|
|
1758
|
+
if (!this._drawer) {
|
|
1759
|
+
this._drawer = document.createElement("pds-drawer");
|
|
1760
|
+
this._drawer.setAttribute("position", "right");
|
|
1761
|
+
this._drawer.setAttribute("show-close", "");
|
|
1762
|
+
this.appendChild(this._drawer);
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (!customElements.get("pds-drawer")) {
|
|
1766
|
+
await customElements.whenDefined("pds-drawer");
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
const header = document.createElement("div");
|
|
1770
|
+
header.setAttribute("slot", "drawer-header");
|
|
1771
|
+
header.className = "flex items-center justify-between";
|
|
1772
|
+
header.textContent = "Design settings";
|
|
1773
|
+
|
|
1774
|
+
const content = document.createElement("div");
|
|
1775
|
+
content.setAttribute("slot", "drawer-content");
|
|
1776
|
+
content.className = "stack-md";
|
|
1777
|
+
|
|
1778
|
+
const presetCard = document.createElement("section");
|
|
1779
|
+
presetCard.className = "card surface-elevated stack-sm";
|
|
1780
|
+
|
|
1781
|
+
const presetTitle = document.createElement("h4");
|
|
1782
|
+
presetTitle.textContent = "Preset";
|
|
1783
|
+
presetCard.appendChild(presetTitle);
|
|
1784
|
+
|
|
1785
|
+
const presetLabel = document.createElement("label");
|
|
1786
|
+
presetLabel.className = "stack-xs";
|
|
1787
|
+
|
|
1788
|
+
const presetText = document.createElement("span");
|
|
1789
|
+
presetText.textContent = "Choose a base style";
|
|
1790
|
+
presetLabel.appendChild(presetText);
|
|
1791
|
+
|
|
1792
|
+
const presetSelect = document.createElement("select");
|
|
1793
|
+
const presetOptions = getPresetOptions();
|
|
1794
|
+
const activePreset = getActivePresetId();
|
|
1795
|
+
|
|
1796
|
+
presetOptions.forEach((preset) => {
|
|
1797
|
+
const option = document.createElement("option");
|
|
1798
|
+
option.value = preset.id;
|
|
1799
|
+
option.textContent = preset.name;
|
|
1800
|
+
if (String(preset.id) === String(activePreset)) {
|
|
1801
|
+
option.selected = true;
|
|
1802
|
+
}
|
|
1803
|
+
presetSelect.appendChild(option);
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
presetSelect.addEventListener("change", async (event) => {
|
|
1807
|
+
const nextPreset = event.target?.value;
|
|
1808
|
+
await applyPresetSelection(nextPreset);
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
presetLabel.appendChild(presetSelect);
|
|
1812
|
+
presetCard.appendChild(presetLabel);
|
|
1813
|
+
|
|
1814
|
+
const themeCard = document.createElement("section");
|
|
1815
|
+
themeCard.className = "card surface-elevated stack-sm";
|
|
1816
|
+
|
|
1817
|
+
const themeTitle = document.createElement("h4");
|
|
1818
|
+
themeTitle.textContent = "Theme";
|
|
1819
|
+
themeCard.appendChild(themeTitle);
|
|
1820
|
+
|
|
1821
|
+
const themeToggle = document.createElement("pds-theme");
|
|
1822
|
+
themeCard.appendChild(themeToggle);
|
|
1823
|
+
|
|
1824
|
+
const searchCard = document.createElement("section");
|
|
1825
|
+
searchCard.className = "card surface-elevated stack-sm";
|
|
1826
|
+
|
|
1827
|
+
const searchTitle = document.createElement("h4");
|
|
1828
|
+
searchTitle.textContent = "Search PDS";
|
|
1829
|
+
searchCard.appendChild(searchTitle);
|
|
1830
|
+
|
|
1831
|
+
const omnibox = document.createElement("pds-omnibox");
|
|
1832
|
+
omnibox.setAttribute("placeholder", "Search tokens, utilities, components...");
|
|
1833
|
+
omnibox.settings = {
|
|
1834
|
+
iconHandler: (item) => {
|
|
1835
|
+
return item.icon ? `<pds-icon icon="${item.icon}"></pds-icon>` : null;
|
|
1836
|
+
},
|
|
1837
|
+
categories: {
|
|
1838
|
+
Query: {
|
|
1839
|
+
trigger: (options) => options.search.length >= 2,
|
|
1840
|
+
getItems: async (options) => {
|
|
1841
|
+
const query = (options.search || "").trim();
|
|
1842
|
+
if (!query) return [];
|
|
1843
|
+
try {
|
|
1844
|
+
const results = await PDS.query(query);
|
|
1845
|
+
return (results || []).map((result) => ({
|
|
1846
|
+
text: result.text,
|
|
1847
|
+
id: result.value,
|
|
1848
|
+
icon: result.icon || "magnifying-glass",
|
|
1849
|
+
category: result.category,
|
|
1850
|
+
code: result.code,
|
|
1851
|
+
}));
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
console.warn("Omnibox query failed:", error);
|
|
1854
|
+
return [];
|
|
1855
|
+
}
|
|
1856
|
+
},
|
|
1857
|
+
action: async (options) => {
|
|
1858
|
+
if (options?.code && navigator.clipboard) {
|
|
1859
|
+
await navigator.clipboard.writeText(options.code);
|
|
1860
|
+
await PDS.toast("Copied token to clipboard", { type: "success" });
|
|
1861
|
+
}
|
|
1862
|
+
},
|
|
1863
|
+
},
|
|
1864
|
+
},
|
|
1865
|
+
};
|
|
1866
|
+
searchCard.appendChild(omnibox);
|
|
1867
|
+
|
|
1868
|
+
content.appendChild(presetCard);
|
|
1869
|
+
content.appendChild(themeCard);
|
|
1870
|
+
content.appendChild(searchCard);
|
|
1871
|
+
|
|
1872
|
+
this._drawer.replaceChildren(header, content);
|
|
1873
|
+
|
|
1874
|
+
if (typeof this._drawer.openDrawer === "function") {
|
|
1875
|
+
this._drawer.openDrawer();
|
|
1876
|
+
} else {
|
|
1877
|
+
this._drawer.setAttribute("open", "");
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
async _handleFormSubmit(event, form) {
|
|
1882
|
+
if (!form || typeof form.getValuesFlat !== "function") return;
|
|
1883
|
+
|
|
1884
|
+
// Save current STORED config (preset + overrides) to undo stack before applying
|
|
1885
|
+
const storedConfig = getStoredConfig() || { preset: null, design: {} };
|
|
1886
|
+
try {
|
|
1887
|
+
this._undoStack.push(structuredClone(storedConfig));
|
|
1888
|
+
} catch (e) {
|
|
1889
|
+
// Fallback for environments without structuredClone
|
|
1890
|
+
this._undoStack.push(JSON.parse(JSON.stringify(storedConfig)));
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// Limit undo stack size
|
|
1894
|
+
if (this._undoStack.length > 10) {
|
|
1895
|
+
this._undoStack.shift();
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
// Apply the changes
|
|
1899
|
+
const flatValues = form.getValuesFlat();
|
|
1900
|
+
const patch = {};
|
|
1901
|
+
Object.entries(flatValues || {}).forEach(([path, value]) => {
|
|
1902
|
+
setValueAtJsonPath(patch, path, value);
|
|
1903
|
+
});
|
|
1904
|
+
await applyDesignPatch(patch);
|
|
1905
|
+
|
|
1906
|
+
// Enable undo button
|
|
1907
|
+
if (form._undoBtn) {
|
|
1908
|
+
form._undoBtn.disabled = false;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
async _handleUndo() {
|
|
1913
|
+
if (this._undoStack.length === 0) return;
|
|
1914
|
+
|
|
1915
|
+
// Get the previous stored config (preset + overrides)
|
|
1916
|
+
const previousConfig = this._undoStack.pop();
|
|
1917
|
+
|
|
1918
|
+
// Update localStorage to fully replace with previous config
|
|
1919
|
+
setStoredConfig(previousConfig);
|
|
1920
|
+
|
|
1921
|
+
// Apply an empty patch to trigger regeneration with the restored design
|
|
1922
|
+
await applyDesignPatch({});
|
|
1923
|
+
|
|
1924
|
+
// Re-render the form with the current dropdown's form container
|
|
1925
|
+
if (this._activeDropdown) {
|
|
1926
|
+
const formContainer = this._activeDropdown.querySelector('.pds-live-editor-form-container');
|
|
1927
|
+
const footer = this._activeDropdown.querySelector('.pds-live-editor-footer');
|
|
1928
|
+
if (formContainer && footer) {
|
|
1929
|
+
// Get the actual current design after applyDesignPatch has run
|
|
1930
|
+
const currentDesign = shallowClone(PDS?.currentConfig?.design || {});
|
|
1931
|
+
const quickContext = collectQuickContext(this._activeTarget);
|
|
1932
|
+
const limitedPaths = quickContext.paths.slice(0, QUICK_EDIT_LIMIT);
|
|
1933
|
+
const quickPaths = quickContext.paths;
|
|
1934
|
+
await this._renderQuickForm(formContainer, footer, limitedPaths, currentDesign, quickContext.hints, this._activeTarget, quickPaths);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1555
1940
|
customElements.define(EDITOR_TAG, PdsLiveEdit);
|