@rogieking/figui3 2.0.1 → 2.0.3
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/components.css +57 -17
- package/example.html +63 -25
- package/fig.js +113 -65
- package/package.json +1 -1
package/components.css
CHANGED
|
@@ -290,8 +290,8 @@
|
|
|
290
290
|
rgba(255, 0, 48, 1)
|
|
291
291
|
);
|
|
292
292
|
|
|
293
|
-
/* Icons -
|
|
294
|
-
--icon-chevron: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.87868 7.12132L8 9.24264L10.1213 7.12132' stroke='
|
|
293
|
+
/* Icons - colorless shapes for use with mask-image */
|
|
294
|
+
--icon-chevron: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.87868 7.12132L8 9.24264L10.1213 7.12132' stroke='black' stroke-linecap='round'/%3E%3C/svg%3E%0A");
|
|
295
295
|
--icon-checkmark: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.7773 4.08403C12.0071 4.2372 12.0692 4.54764 11.916 4.7774L7.91603 10.7774C7.83293 10.902 7.69834 10.9829 7.54927 10.9976C7.4002 11.0124 7.25237 10.9595 7.14645 10.8536L4.14645 7.85361C3.95118 7.65834 3.95118 7.34176 4.14645 7.1465C4.34171 6.95124 4.65829 6.95124 4.85355 7.1465L7.42229 9.71523L11.084 4.2227C11.2372 3.99294 11.5476 3.93085 11.7773 4.08403Z' fill='white'/%3E%3C/svg%3E%0A");
|
|
296
296
|
--icon-steppers: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11.7245 6.08191C11.9186 5.95386 12.1826 5.97562 12.3534 6.14637L14.3534 8.14637L14.4179 8.22449C14.546 8.41852 14.5242 8.68253 14.3534 8.8534C14.1826 9.02426 13.9186 9.04601 13.7245 8.91785L13.6464 8.8534L11.9999 7.20691L10.3534 8.8534C10.1582 9.04866 9.84166 9.04866 9.6464 8.8534C9.45123 8.65813 9.45117 8.3416 9.6464 8.14637L11.6464 6.14637L11.7245 6.08191Z' fill='black'/%3E%3Cpath d='M13.7248 15.0822C13.9189 14.9541 14.1829 14.9758 14.3537 15.1467C14.5246 15.3176 14.5463 15.5815 14.4182 15.7756L14.3537 15.8537L12.3537 17.8537C12.1829 18.0246 11.9189 18.0463 11.7248 17.9182L11.6467 17.8537L9.64669 15.8537L9.58224 15.7756C9.45407 15.5815 9.47583 15.3176 9.64669 15.1467C9.81756 14.9758 10.0815 14.9541 10.2756 15.0822L10.3537 15.1467L12.0002 16.7932L13.6467 15.1467L13.7248 15.0822Z' fill='black' /%3E%3C/svg%3E%0A");
|
|
297
297
|
|
|
@@ -309,11 +309,10 @@
|
|
|
309
309
|
--handle-shadow: 0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
/* Dark theme overrides for non-color values (
|
|
313
|
-
/* These cannot use light-dark() as they are
|
|
312
|
+
/* Dark theme overrides for non-color values (shadows, elevations) */
|
|
313
|
+
/* These cannot use light-dark() as they are complex values */
|
|
314
314
|
@media (prefers-color-scheme: dark) {
|
|
315
315
|
:root {
|
|
316
|
-
--icon-chevron: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.87868 7.12132L8 9.24264L10.1213 7.12132' stroke='rgb(255 255 255 / 100%)' stroke-linecap='round'/%3E%3C/svg%3E%0A");
|
|
317
316
|
--handle-shadow: 0px 0 0 0.75px 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
|
|
318
317
|
0px 0px 0.5px 0px rgba(255, 255, 255, 0.1);
|
|
319
318
|
--figma-elevation-100: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
|
@@ -332,7 +331,6 @@
|
|
|
332
331
|
|
|
333
332
|
/* Class-based dark theme override (for manual theme switching) */
|
|
334
333
|
:root.figma-dark {
|
|
335
|
-
--icon-chevron: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5.87868 7.12132L8 9.24264L10.1213 7.12132' stroke='rgb(255 255 255 / 100%)' stroke-linecap='round'/%3E%3C/svg%3E%0A");
|
|
336
334
|
--handle-shadow: 0px 0 0 0.75px 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
|
|
337
335
|
0px 0px 0.5px 0px rgba(255, 255, 255, 0.1);
|
|
338
336
|
--figma-elevation-100: 0px 0px 0.5px 0px rgba(0, 0, 0, 0.5),
|
|
@@ -613,15 +611,6 @@ input[type="text"][list] {
|
|
|
613
611
|
}
|
|
614
612
|
}
|
|
615
613
|
|
|
616
|
-
input[type="text"][list]:hover,
|
|
617
|
-
input[type="text"][list]:active,
|
|
618
|
-
input[type="text"][list]:focus,
|
|
619
|
-
select {
|
|
620
|
-
background-image: var(--icon-chevron);
|
|
621
|
-
background-position: calc(100% - 0.25rem) center;
|
|
622
|
-
background-repeat: no-repeat;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
614
|
input[type="text"][list],
|
|
626
615
|
select {
|
|
627
616
|
padding-right: 1.5rem;
|
|
@@ -640,6 +629,23 @@ fig-dropdown,
|
|
|
640
629
|
display: block;
|
|
641
630
|
width: 100%;
|
|
642
631
|
}
|
|
632
|
+
|
|
633
|
+
/* Chevron icon using mask-image for light-dark support */
|
|
634
|
+
&::after {
|
|
635
|
+
content: "";
|
|
636
|
+
position: absolute;
|
|
637
|
+
right: 0.25rem;
|
|
638
|
+
top: 50%;
|
|
639
|
+
transform: translateY(-50%);
|
|
640
|
+
width: 1rem;
|
|
641
|
+
height: 1rem;
|
|
642
|
+
mask-image: var(--icon-chevron);
|
|
643
|
+
mask-size: contain;
|
|
644
|
+
mask-repeat: no-repeat;
|
|
645
|
+
mask-position: center;
|
|
646
|
+
background-color: var(--figma-color-icon);
|
|
647
|
+
pointer-events: none;
|
|
648
|
+
}
|
|
643
649
|
}
|
|
644
650
|
|
|
645
651
|
/* Button */
|
|
@@ -1546,7 +1552,7 @@ fig-slider {
|
|
|
1546
1552
|
--slider-tick-size: calc(var(--slider-height) / 4);
|
|
1547
1553
|
--slider-handle-shadow: inset 0 0 0 calc(4px + 0.5rem * var(--unchanged))
|
|
1548
1554
|
var(--handle-color),
|
|
1549
|
-
var(--
|
|
1555
|
+
0px 0 0 0.5px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
|
|
1550
1556
|
--slider-handle-shadow-focus: inset 0 0 0 4px var(--handle-color),
|
|
1551
1557
|
inset 0 0 0 5px rgba(0, 0, 0, 0.1), var(--handle-shadow),
|
|
1552
1558
|
0 0 0 1px var(--figma-color-border-selected);
|
|
@@ -2241,11 +2247,23 @@ fig-input-number {
|
|
|
2241
2247
|
justify-content: center;
|
|
2242
2248
|
user-select: all;
|
|
2243
2249
|
gap: 0;
|
|
2250
|
+
min-width: 0;
|
|
2251
|
+
width: 8rem; /* Default width */
|
|
2252
|
+
flex-shrink: 1;
|
|
2253
|
+
flex-grow: 0;
|
|
2244
2254
|
color: var(--figma-color-text);
|
|
2245
2255
|
|
|
2256
|
+
/* When inside a flex container (like fig-field), grow to fill */
|
|
2257
|
+
fig-field &,
|
|
2258
|
+
[style*="flex"] &,
|
|
2259
|
+
[style*="display: flex"] & {
|
|
2260
|
+
width: auto;
|
|
2261
|
+
flex: 1 1 auto;
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2246
2264
|
&[multiline] {
|
|
2247
|
-
display: block;
|
|
2248
2265
|
display: flex;
|
|
2266
|
+
width: 100%; /* Multiline defaults to full width */
|
|
2249
2267
|
}
|
|
2250
2268
|
&[autoresize] input,
|
|
2251
2269
|
&[autoresize] textarea {
|
|
@@ -2442,6 +2460,17 @@ fig-input-joystick {
|
|
|
2442
2460
|
display: inline-flex;
|
|
2443
2461
|
gap: var(--spacer-2);
|
|
2444
2462
|
user-select: none;
|
|
2463
|
+
|
|
2464
|
+
/* When inside horizontal fig-field, grow to fill and let inputs expand */
|
|
2465
|
+
fig-field[direction="horizontal"] & {
|
|
2466
|
+
flex: 1;
|
|
2467
|
+
min-width: 0;
|
|
2468
|
+
|
|
2469
|
+
fig-input-number {
|
|
2470
|
+
flex: 1;
|
|
2471
|
+
width: auto;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2445
2474
|
.fig-input-joystick-plane-container {
|
|
2446
2475
|
display: flex;
|
|
2447
2476
|
width: 1.5rem;
|
|
@@ -2541,6 +2570,17 @@ fig-input-angle {
|
|
|
2541
2570
|
gap: var(--spacer-2);
|
|
2542
2571
|
user-select: none;
|
|
2543
2572
|
|
|
2573
|
+
/* When inside horizontal fig-field, grow to fill and let inputs expand */
|
|
2574
|
+
fig-field[direction="horizontal"] & {
|
|
2575
|
+
flex: 1;
|
|
2576
|
+
min-width: 0;
|
|
2577
|
+
|
|
2578
|
+
fig-input-number {
|
|
2579
|
+
flex: 1;
|
|
2580
|
+
width: auto;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2544
2584
|
.fig-input-angle-plane {
|
|
2545
2585
|
display: inline-grid;
|
|
2546
2586
|
place-items: center;
|
package/example.html
CHANGED
|
@@ -768,22 +768,6 @@
|
|
|
768
768
|
</optgroup>
|
|
769
769
|
</fig-dropdown>
|
|
770
770
|
|
|
771
|
-
<h3>Variants</h3>
|
|
772
|
-
<div class="example-row">
|
|
773
|
-
<label>Default</label>
|
|
774
|
-
<fig-dropdown>
|
|
775
|
-
<option>Default Style</option>
|
|
776
|
-
<option>Option Two</option>
|
|
777
|
-
</fig-dropdown>
|
|
778
|
-
</div>
|
|
779
|
-
<div class="example-row">
|
|
780
|
-
<label>Neue</label>
|
|
781
|
-
<fig-dropdown variant="neue">
|
|
782
|
-
<option>Neue Style</option>
|
|
783
|
-
<option>Option Two</option>
|
|
784
|
-
</fig-dropdown>
|
|
785
|
-
</div>
|
|
786
|
-
|
|
787
771
|
<h3>Full Width</h3>
|
|
788
772
|
<fig-dropdown full>
|
|
789
773
|
<option>Full Width Dropdown</option>
|
|
@@ -811,29 +795,65 @@
|
|
|
811
795
|
style="margin-top: 24px;">
|
|
812
796
|
<label>Horizontal</label>
|
|
813
797
|
</div>
|
|
814
|
-
<fig-field direction="horizontal">
|
|
798
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
815
799
|
<label>Field Label</label>
|
|
816
800
|
<fig-input-text placeholder="Enter text"></fig-input-text>
|
|
817
801
|
</fig-field>
|
|
818
802
|
|
|
819
|
-
<h3>
|
|
820
|
-
<fig-field direction="horizontal">
|
|
803
|
+
<h3>Horizontal Fields with Different Inputs</h3>
|
|
804
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
805
|
+
<label>Text Input</label>
|
|
806
|
+
<fig-input-text placeholder="Enter text"></fig-input-text>
|
|
807
|
+
</fig-field>
|
|
808
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
809
|
+
<label>Number Input</label>
|
|
810
|
+
<fig-input-number value="100"
|
|
811
|
+
units="px"></fig-input-number>
|
|
812
|
+
</fig-field>
|
|
813
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
821
814
|
<label>Dropdown</label>
|
|
822
815
|
<fig-dropdown>
|
|
823
816
|
<option>Option One</option>
|
|
824
817
|
<option>Option Two</option>
|
|
818
|
+
<option>Option Three</option>
|
|
825
819
|
</fig-dropdown>
|
|
826
820
|
</fig-field>
|
|
827
|
-
<fig-field direction="horizontal">
|
|
821
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
828
822
|
<label>Slider</label>
|
|
829
823
|
<fig-slider value="50"
|
|
830
824
|
text="true"></fig-slider>
|
|
831
825
|
</fig-field>
|
|
832
|
-
<fig-field direction="horizontal">
|
|
826
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
833
827
|
<label>Color</label>
|
|
834
828
|
<fig-input-color value="#0D99FF"
|
|
835
829
|
text="true"></fig-input-color>
|
|
836
830
|
</fig-field>
|
|
831
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
832
|
+
<label>Angle</label>
|
|
833
|
+
<fig-input-angle value="45"
|
|
834
|
+
text="true"></fig-input-angle>
|
|
835
|
+
</fig-field>
|
|
836
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
837
|
+
<label>Joystick</label>
|
|
838
|
+
<fig-input-joystick text="true"
|
|
839
|
+
value="50,50"></fig-input-joystick>
|
|
840
|
+
</fig-field>
|
|
841
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
842
|
+
<label>Switch</label>
|
|
843
|
+
<fig-switch></fig-switch>
|
|
844
|
+
</fig-field>
|
|
845
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
846
|
+
<label>Checkbox</label>
|
|
847
|
+
<fig-checkbox></fig-checkbox>
|
|
848
|
+
</fig-field>
|
|
849
|
+
<fig-field direction="horizontal" style="width: 240px;">
|
|
850
|
+
<label>Segmented</label>
|
|
851
|
+
<fig-segmented-control>
|
|
852
|
+
<fig-segment value="left">Left</fig-segment>
|
|
853
|
+
<fig-segment value="center">Center</fig-segment>
|
|
854
|
+
<fig-segment value="right">Right</fig-segment>
|
|
855
|
+
</fig-segmented-control>
|
|
856
|
+
</fig-field>
|
|
837
857
|
</section>
|
|
838
858
|
<hr>
|
|
839
859
|
|
|
@@ -978,16 +998,34 @@
|
|
|
978
998
|
<h2>Input Joystick</h2>
|
|
979
999
|
<p class="description">A 2D joystick input for X/Y coordinate values.</p>
|
|
980
1000
|
|
|
981
|
-
<h3>Default</h3>
|
|
982
|
-
<fig-input-joystick></fig-input-joystick>
|
|
1001
|
+
<h3>Default (centered)</h3>
|
|
1002
|
+
<fig-input-joystick value="50,50"></fig-input-joystick>
|
|
983
1003
|
|
|
984
1004
|
<h3>With Text Display</h3>
|
|
985
1005
|
<fig-input-joystick text="true"
|
|
986
1006
|
value="0,0"></fig-input-joystick>
|
|
987
1007
|
|
|
988
|
-
<h3>With Initial Value</h3>
|
|
1008
|
+
<h3>With Initial Value (25%, 75%)</h3>
|
|
989
1009
|
<fig-input-joystick text="true"
|
|
990
|
-
value="
|
|
1010
|
+
value="25,75"></fig-input-joystick>
|
|
1011
|
+
|
|
1012
|
+
<h3>Screen Coordinates (default: 0,0 = top-left)</h3>
|
|
1013
|
+
<fig-input-joystick text="true"
|
|
1014
|
+
coordinates="screen"
|
|
1015
|
+
value="0,0"></fig-input-joystick>
|
|
1016
|
+
|
|
1017
|
+
<h3>Math Coordinates (0,0 = bottom-left)</h3>
|
|
1018
|
+
<fig-input-joystick text="true"
|
|
1019
|
+
coordinates="math"
|
|
1020
|
+
value="0,0"></fig-input-joystick>
|
|
1021
|
+
|
|
1022
|
+
<h3>With Field Label</h3>
|
|
1023
|
+
<fig-field direction="horizontal"
|
|
1024
|
+
style="width: 240px;">
|
|
1025
|
+
<label>Origin</label>
|
|
1026
|
+
<fig-input-joystick text="true"
|
|
1027
|
+
value="50,50"></fig-input-joystick>
|
|
1028
|
+
</fig-field>
|
|
991
1029
|
</section>
|
|
992
1030
|
<hr>
|
|
993
1031
|
|
package/fig.js
CHANGED
|
@@ -653,14 +653,14 @@ class FigDialog extends HTMLDialogElement {
|
|
|
653
653
|
|
|
654
654
|
// Apply common styles
|
|
655
655
|
this.style.position = "fixed";
|
|
656
|
-
this.style.
|
|
656
|
+
this.style.transform = "none";
|
|
657
657
|
|
|
658
658
|
// Reset position properties
|
|
659
659
|
this.style.top = "auto";
|
|
660
660
|
this.style.bottom = "auto";
|
|
661
661
|
this.style.left = "auto";
|
|
662
662
|
this.style.right = "auto";
|
|
663
|
-
this.style.
|
|
663
|
+
this.style.margin = "0";
|
|
664
664
|
|
|
665
665
|
// Parse position attribute
|
|
666
666
|
const hasTop = position.includes("top");
|
|
@@ -676,7 +676,8 @@ class FigDialog extends HTMLDialogElement {
|
|
|
676
676
|
} else if (hasBottom) {
|
|
677
677
|
this.style.bottom = `${this.#offset}px`;
|
|
678
678
|
} else if (hasVCenter) {
|
|
679
|
-
this.style.top = "
|
|
679
|
+
this.style.top = "0";
|
|
680
|
+
this.style.bottom = "0";
|
|
680
681
|
}
|
|
681
682
|
|
|
682
683
|
// Horizontal positioning
|
|
@@ -685,16 +686,19 @@ class FigDialog extends HTMLDialogElement {
|
|
|
685
686
|
} else if (hasRight) {
|
|
686
687
|
this.style.right = `${this.#offset}px`;
|
|
687
688
|
} else if (hasHCenter) {
|
|
688
|
-
this.style.left = "
|
|
689
|
+
this.style.left = "0";
|
|
690
|
+
this.style.right = "0";
|
|
689
691
|
}
|
|
690
692
|
|
|
691
|
-
// Apply
|
|
693
|
+
// Apply margin auto for centering (works without knowing dimensions)
|
|
692
694
|
if (hasVCenter && hasHCenter) {
|
|
693
|
-
this.style.
|
|
695
|
+
this.style.margin = "auto";
|
|
694
696
|
} else if (hasVCenter) {
|
|
695
|
-
this.style.
|
|
697
|
+
this.style.marginTop = "auto";
|
|
698
|
+
this.style.marginBottom = "auto";
|
|
696
699
|
} else if (hasHCenter) {
|
|
697
|
-
this.style.
|
|
700
|
+
this.style.marginLeft = "auto";
|
|
701
|
+
this.style.marginRight = "auto";
|
|
698
702
|
}
|
|
699
703
|
|
|
700
704
|
this.#positionInitialized = true;
|
|
@@ -718,7 +722,7 @@ class FigDialog extends HTMLDialogElement {
|
|
|
718
722
|
}
|
|
719
723
|
|
|
720
724
|
#isInteractiveElement(element) {
|
|
721
|
-
//
|
|
725
|
+
// Standard HTML interactive elements
|
|
722
726
|
const interactiveSelectors = [
|
|
723
727
|
"input",
|
|
724
728
|
"button",
|
|
@@ -728,26 +732,30 @@ class FigDialog extends HTMLDialogElement {
|
|
|
728
732
|
"label",
|
|
729
733
|
'[contenteditable="true"]',
|
|
730
734
|
"[tabindex]",
|
|
731
|
-
"fig-button",
|
|
732
|
-
"fig-input-text",
|
|
733
|
-
"fig-input-number",
|
|
734
|
-
"fig-slider",
|
|
735
|
-
"fig-checkbox",
|
|
736
|
-
"fig-radio",
|
|
737
|
-
"fig-tab",
|
|
738
|
-
"fig-dropdown",
|
|
739
|
-
"fig-chit",
|
|
740
735
|
];
|
|
741
736
|
|
|
737
|
+
// Non-interactive fig-* container elements (should allow dragging)
|
|
738
|
+
const nonInteractiveFigElements = [
|
|
739
|
+
"FIG-HEADER",
|
|
740
|
+
"FIG-DIALOG",
|
|
741
|
+
"FIG-FIELD",
|
|
742
|
+
"FIG-TOOLTIP",
|
|
743
|
+
];
|
|
744
|
+
|
|
745
|
+
const isInteractive = (el) =>
|
|
746
|
+
interactiveSelectors.some((selector) => el.matches?.(selector)) ||
|
|
747
|
+
(el.tagName?.startsWith("FIG-") &&
|
|
748
|
+
!nonInteractiveFigElements.includes(el.tagName));
|
|
749
|
+
|
|
742
750
|
// Check if the element itself is interactive
|
|
743
|
-
if (
|
|
751
|
+
if (isInteractive(element)) {
|
|
744
752
|
return true;
|
|
745
753
|
}
|
|
746
754
|
|
|
747
755
|
// Check if any parent element up to the dialog is interactive
|
|
748
756
|
let parent = element.parentElement;
|
|
749
757
|
while (parent && parent !== this) {
|
|
750
|
-
if (
|
|
758
|
+
if (isInteractive(parent)) {
|
|
751
759
|
return true;
|
|
752
760
|
}
|
|
753
761
|
parent = parent.parentElement;
|
|
@@ -767,11 +775,13 @@ class FigDialog extends HTMLDialogElement {
|
|
|
767
775
|
// Get current position from computed style
|
|
768
776
|
const rect = this.getBoundingClientRect();
|
|
769
777
|
|
|
770
|
-
//
|
|
778
|
+
// Convert to pixel-based top/left positioning for dragging
|
|
779
|
+
// (clears margin: auto centering)
|
|
771
780
|
this.style.top = `${rect.top}px`;
|
|
772
781
|
this.style.left = `${rect.left}px`;
|
|
773
782
|
this.style.bottom = "auto";
|
|
774
783
|
this.style.right = "auto";
|
|
784
|
+
this.style.margin = "0";
|
|
775
785
|
|
|
776
786
|
// Store offset from pointer to dialog top-left corner
|
|
777
787
|
this.#dragOffset.x = e.clientX - rect.left;
|
|
@@ -2625,10 +2635,15 @@ class FigComboInput extends HTMLElement {
|
|
|
2625
2635
|
}
|
|
2626
2636
|
if (name === "placeholder") {
|
|
2627
2637
|
this.placeholder = newValue;
|
|
2638
|
+
if (this.input) {
|
|
2639
|
+
this.input.setAttribute("placeholder", newValue);
|
|
2640
|
+
}
|
|
2628
2641
|
}
|
|
2629
2642
|
if (name === "value") {
|
|
2630
2643
|
this.value = newValue;
|
|
2631
|
-
this.input
|
|
2644
|
+
if (this.input) {
|
|
2645
|
+
this.input.setAttribute("value", newValue);
|
|
2646
|
+
}
|
|
2632
2647
|
}
|
|
2633
2648
|
}
|
|
2634
2649
|
}
|
|
@@ -2908,15 +2923,20 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2908
2923
|
constructor() {
|
|
2909
2924
|
super();
|
|
2910
2925
|
|
|
2911
|
-
this.position = { x: 0.5, y: 0.5 };
|
|
2912
|
-
this.value = [0.5, 0.5];
|
|
2926
|
+
this.position = { x: 0.5, y: 0.5 }; // Internal position (0-1)
|
|
2913
2927
|
this.isDragging = false;
|
|
2914
2928
|
this.isShiftHeld = false;
|
|
2915
2929
|
this.plane = null;
|
|
2916
2930
|
this.cursor = null;
|
|
2917
2931
|
this.xInput = null;
|
|
2918
2932
|
this.yInput = null;
|
|
2933
|
+
this.coordinates = "screen"; // "screen" (0,0 top-left) or "math" (0,0 bottom-left)
|
|
2934
|
+
this.#initialized = false;
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
#initialized = false;
|
|
2919
2938
|
|
|
2939
|
+
connectedCallback() {
|
|
2920
2940
|
// Initialize position
|
|
2921
2941
|
requestAnimationFrame(() => {
|
|
2922
2942
|
this.precision = this.getAttribute("precision") || 3;
|
|
@@ -2924,24 +2944,19 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2924
2944
|
this.transform = this.getAttribute("transform") || 1;
|
|
2925
2945
|
this.transform = Number(this.transform);
|
|
2926
2946
|
this.text = this.getAttribute("text") === "true";
|
|
2947
|
+
this.coordinates = this.getAttribute("coordinates") || "screen";
|
|
2927
2948
|
|
|
2928
2949
|
this.#render();
|
|
2929
|
-
|
|
2930
2950
|
this.#setupListeners();
|
|
2931
|
-
|
|
2932
2951
|
this.#syncHandlePosition();
|
|
2933
|
-
|
|
2934
|
-
this.xInput.setAttribute(
|
|
2935
|
-
"value",
|
|
2936
|
-
this.position.x.toFixed(this.precision)
|
|
2937
|
-
);
|
|
2938
|
-
this.yInput.setAttribute(
|
|
2939
|
-
"value",
|
|
2940
|
-
this.position.y.toFixed(this.precision)
|
|
2941
|
-
);
|
|
2942
|
-
}
|
|
2952
|
+
this.#initialized = true;
|
|
2943
2953
|
});
|
|
2944
2954
|
}
|
|
2955
|
+
|
|
2956
|
+
// Convert Y for display (CSS uses top-down, math uses bottom-up)
|
|
2957
|
+
#displayY(y) {
|
|
2958
|
+
return this.coordinates === "math" ? 1 - y : y;
|
|
2959
|
+
}
|
|
2945
2960
|
disconnectedCallback() {
|
|
2946
2961
|
this.#cleanupListeners();
|
|
2947
2962
|
}
|
|
@@ -2959,24 +2974,24 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2959
2974
|
</div>
|
|
2960
2975
|
${
|
|
2961
2976
|
this.text
|
|
2962
|
-
? `<fig-input-
|
|
2963
|
-
type="number"
|
|
2977
|
+
? `<fig-input-number
|
|
2964
2978
|
name="x"
|
|
2965
|
-
step="
|
|
2966
|
-
value="${this.position.x}"
|
|
2979
|
+
step="1"
|
|
2980
|
+
value="${this.position.x * 100}"
|
|
2967
2981
|
min="0"
|
|
2968
|
-
max="
|
|
2982
|
+
max="100"
|
|
2983
|
+
units="%">
|
|
2969
2984
|
<span slot="prepend">X</span>
|
|
2970
|
-
</fig-input-
|
|
2971
|
-
<fig-input-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2985
|
+
</fig-input-number>
|
|
2986
|
+
<fig-input-number
|
|
2987
|
+
name="y"
|
|
2988
|
+
step="1"
|
|
2989
|
+
min="0"
|
|
2990
|
+
max="100"
|
|
2991
|
+
value="${this.position.y * 100}"
|
|
2992
|
+
units="%">
|
|
2978
2993
|
<span slot="prepend">Y</span>
|
|
2979
|
-
</fig-input-
|
|
2994
|
+
</fig-input-number>`
|
|
2980
2995
|
: ""
|
|
2981
2996
|
}
|
|
2982
2997
|
`;
|
|
@@ -2985,8 +3000,8 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2985
3000
|
#setupListeners() {
|
|
2986
3001
|
this.plane = this.querySelector(".fig-input-joystick-plane");
|
|
2987
3002
|
this.cursor = this.querySelector(".fig-input-joystick-handle");
|
|
2988
|
-
this.xInput = this.querySelector("fig-input-
|
|
2989
|
-
this.yInput = this.querySelector("fig-input-
|
|
3003
|
+
this.xInput = this.querySelector("fig-input-number[name='x']");
|
|
3004
|
+
this.yInput = this.querySelector("fig-input-number[name='y']");
|
|
2990
3005
|
this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
|
|
2991
3006
|
this.plane.addEventListener(
|
|
2992
3007
|
"touchstart",
|
|
@@ -3001,7 +3016,10 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3001
3016
|
}
|
|
3002
3017
|
|
|
3003
3018
|
#cleanupListeners() {
|
|
3004
|
-
this.plane
|
|
3019
|
+
if (this.plane) {
|
|
3020
|
+
this.plane.removeEventListener("mousedown", this.#handleMouseDown);
|
|
3021
|
+
this.plane.removeEventListener("touchstart", this.#handleTouchStart);
|
|
3022
|
+
}
|
|
3005
3023
|
window.removeEventListener("keydown", this.#handleKeyDown);
|
|
3006
3024
|
window.removeEventListener("keyup", this.#handleKeyUp);
|
|
3007
3025
|
if (this.text && this.xInput && this.yInput) {
|
|
@@ -3012,7 +3030,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3012
3030
|
|
|
3013
3031
|
#handleXInput(e) {
|
|
3014
3032
|
e.stopPropagation();
|
|
3015
|
-
this.position.x = Number(e.target.value);
|
|
3033
|
+
this.position.x = Number(e.target.value) / 100; // Convert from percentage to decimal
|
|
3016
3034
|
this.#syncHandlePosition();
|
|
3017
3035
|
this.#emitInputEvent();
|
|
3018
3036
|
this.#emitChangeEvent();
|
|
@@ -3020,7 +3038,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3020
3038
|
|
|
3021
3039
|
#handleYInput(e) {
|
|
3022
3040
|
e.stopPropagation();
|
|
3023
|
-
this.position.y = Number(e.target.value);
|
|
3041
|
+
this.position.y = Number(e.target.value) / 100; // Convert from percentage to decimal
|
|
3024
3042
|
this.#syncHandlePosition();
|
|
3025
3043
|
this.#emitInputEvent();
|
|
3026
3044
|
this.#emitChangeEvent();
|
|
@@ -3045,7 +3063,13 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3045
3063
|
#updatePosition(e) {
|
|
3046
3064
|
const rect = this.plane.getBoundingClientRect();
|
|
3047
3065
|
let x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
|
3048
|
-
let
|
|
3066
|
+
let screenY = Math.max(
|
|
3067
|
+
0,
|
|
3068
|
+
Math.min(1, (e.clientY - rect.top) / rect.height)
|
|
3069
|
+
);
|
|
3070
|
+
|
|
3071
|
+
// Convert screen Y to internal Y (flip for math coordinates)
|
|
3072
|
+
let y = this.coordinates === "math" ? 1 - screenY : screenY;
|
|
3049
3073
|
|
|
3050
3074
|
x = this.#snapToGuide(x);
|
|
3051
3075
|
y = this.#snapToGuide(y);
|
|
@@ -3053,11 +3077,12 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3053
3077
|
const snapped = this.#snapToDiagonal(x, y);
|
|
3054
3078
|
this.position = snapped;
|
|
3055
3079
|
|
|
3080
|
+
const displayY = this.#displayY(snapped.y);
|
|
3056
3081
|
this.cursor.style.left = `${snapped.x * 100}%`;
|
|
3057
|
-
this.cursor.style.top = `${
|
|
3082
|
+
this.cursor.style.top = `${displayY * 100}%`;
|
|
3058
3083
|
if (this.text && this.xInput && this.yInput) {
|
|
3059
|
-
this.xInput.setAttribute("value", snapped.x
|
|
3060
|
-
this.yInput.setAttribute("value", snapped.y
|
|
3084
|
+
this.xInput.setAttribute("value", Math.round(snapped.x * 100));
|
|
3085
|
+
this.yInput.setAttribute("value", Math.round(snapped.y * 100));
|
|
3061
3086
|
}
|
|
3062
3087
|
|
|
3063
3088
|
this.#emitInputEvent();
|
|
@@ -3082,9 +3107,15 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3082
3107
|
}
|
|
3083
3108
|
|
|
3084
3109
|
#syncHandlePosition() {
|
|
3110
|
+
const displayY = this.#displayY(this.position.y);
|
|
3085
3111
|
if (this.cursor) {
|
|
3086
3112
|
this.cursor.style.left = `${this.position.x * 100}%`;
|
|
3087
|
-
this.cursor.style.top = `${
|
|
3113
|
+
this.cursor.style.top = `${displayY * 100}%`;
|
|
3114
|
+
}
|
|
3115
|
+
// Also sync text inputs if they exist (convert to percentage 0-100)
|
|
3116
|
+
if (this.text && this.xInput && this.yInput) {
|
|
3117
|
+
this.xInput.setAttribute("value", Math.round(this.position.x * 100));
|
|
3118
|
+
this.yInput.setAttribute("value", Math.round(this.position.y * 100));
|
|
3088
3119
|
}
|
|
3089
3120
|
}
|
|
3090
3121
|
|
|
@@ -3143,15 +3174,28 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3143
3174
|
if (e.key === "Shift") this.isShiftHeld = false;
|
|
3144
3175
|
}
|
|
3145
3176
|
static get observedAttributes() {
|
|
3146
|
-
return ["value", "precision", "transform", "text"];
|
|
3177
|
+
return ["value", "precision", "transform", "text", "coordinates"];
|
|
3147
3178
|
}
|
|
3148
3179
|
get value() {
|
|
3149
|
-
|
|
3180
|
+
// Return as percentage values (0-100)
|
|
3181
|
+
return [
|
|
3182
|
+
Math.round(this.position.x * 100),
|
|
3183
|
+
Math.round(this.position.y * 100),
|
|
3184
|
+
];
|
|
3150
3185
|
}
|
|
3151
3186
|
set value(value) {
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3187
|
+
// Parse value, strip % symbols if present, convert from 0-100 to 0-1
|
|
3188
|
+
const v = value
|
|
3189
|
+
.toString()
|
|
3190
|
+
.split(",")
|
|
3191
|
+
.map((s) => {
|
|
3192
|
+
const num = parseFloat(s.replace(/%/g, "").trim());
|
|
3193
|
+
return isNaN(num) ? 0.5 : num / 100; // Convert from percentage to decimal, default to 0.5 if invalid
|
|
3194
|
+
});
|
|
3195
|
+
this.position = { x: v[0] ?? 0.5, y: v[1] ?? 0.5 };
|
|
3196
|
+
if (this.#initialized) {
|
|
3197
|
+
this.#syncHandlePosition();
|
|
3198
|
+
}
|
|
3155
3199
|
}
|
|
3156
3200
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
3157
3201
|
if (name === "value") {
|
|
@@ -3167,6 +3211,10 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3167
3211
|
this.text = newValue.toLowerCase() === "true";
|
|
3168
3212
|
this.#render();
|
|
3169
3213
|
}
|
|
3214
|
+
if (name === "coordinates") {
|
|
3215
|
+
this.coordinates = newValue || "screen";
|
|
3216
|
+
this.#syncHandlePosition();
|
|
3217
|
+
}
|
|
3170
3218
|
}
|
|
3171
3219
|
}
|
|
3172
3220
|
|