@rogieking/figui3 2.0.2 → 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 CHANGED
@@ -290,8 +290,8 @@
290
290
  rgba(255, 0, 48, 1)
291
291
  );
292
292
 
293
- /* Icons - light theme defaults */
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='rgb(0 0 0 / 90%)' stroke-linecap='round'/%3E%3C/svg%3E%0A");
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 (icons, shadows, elevations) */
313
- /* These cannot use light-dark() as they are not color values */
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(--handle-shadow);
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>With Different Inputs</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="0.5,0.5"></fig-input-joystick>
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
@@ -2635,10 +2635,15 @@ class FigComboInput extends HTMLElement {
2635
2635
  }
2636
2636
  if (name === "placeholder") {
2637
2637
  this.placeholder = newValue;
2638
+ if (this.input) {
2639
+ this.input.setAttribute("placeholder", newValue);
2640
+ }
2638
2641
  }
2639
2642
  if (name === "value") {
2640
2643
  this.value = newValue;
2641
- this.input.setAttribute("value", newValue);
2644
+ if (this.input) {
2645
+ this.input.setAttribute("value", newValue);
2646
+ }
2642
2647
  }
2643
2648
  }
2644
2649
  }
@@ -2918,15 +2923,20 @@ class FigInputJoystick extends HTMLElement {
2918
2923
  constructor() {
2919
2924
  super();
2920
2925
 
2921
- this.position = { x: 0.5, y: 0.5 };
2922
- this.value = [0.5, 0.5];
2926
+ this.position = { x: 0.5, y: 0.5 }; // Internal position (0-1)
2923
2927
  this.isDragging = false;
2924
2928
  this.isShiftHeld = false;
2925
2929
  this.plane = null;
2926
2930
  this.cursor = null;
2927
2931
  this.xInput = null;
2928
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;
2929
2938
 
2939
+ connectedCallback() {
2930
2940
  // Initialize position
2931
2941
  requestAnimationFrame(() => {
2932
2942
  this.precision = this.getAttribute("precision") || 3;
@@ -2934,24 +2944,19 @@ class FigInputJoystick extends HTMLElement {
2934
2944
  this.transform = this.getAttribute("transform") || 1;
2935
2945
  this.transform = Number(this.transform);
2936
2946
  this.text = this.getAttribute("text") === "true";
2947
+ this.coordinates = this.getAttribute("coordinates") || "screen";
2937
2948
 
2938
2949
  this.#render();
2939
-
2940
2950
  this.#setupListeners();
2941
-
2942
2951
  this.#syncHandlePosition();
2943
- if (this.text && this.xInput && this.yInput) {
2944
- this.xInput.setAttribute(
2945
- "value",
2946
- this.position.x.toFixed(this.precision)
2947
- );
2948
- this.yInput.setAttribute(
2949
- "value",
2950
- this.position.y.toFixed(this.precision)
2951
- );
2952
- }
2952
+ this.#initialized = true;
2953
2953
  });
2954
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
+ }
2955
2960
  disconnectedCallback() {
2956
2961
  this.#cleanupListeners();
2957
2962
  }
@@ -2969,24 +2974,24 @@ class FigInputJoystick extends HTMLElement {
2969
2974
  </div>
2970
2975
  ${
2971
2976
  this.text
2972
- ? `<fig-input-text
2973
- type="number"
2977
+ ? `<fig-input-number
2974
2978
  name="x"
2975
- step="0.01"
2976
- value="${this.position.x}"
2979
+ step="1"
2980
+ value="${this.position.x * 100}"
2977
2981
  min="0"
2978
- max="1">
2982
+ max="100"
2983
+ units="%">
2979
2984
  <span slot="prepend">X</span>
2980
- </fig-input-text>
2981
- <fig-input-text
2982
- type="number"
2983
- name="y"
2984
- step="0.01"
2985
- min="0"
2986
- max="1"
2987
- value="${this.position.y}">
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="%">
2988
2993
  <span slot="prepend">Y</span>
2989
- </fig-input-text>`
2994
+ </fig-input-number>`
2990
2995
  : ""
2991
2996
  }
2992
2997
  `;
@@ -2995,8 +3000,8 @@ class FigInputJoystick extends HTMLElement {
2995
3000
  #setupListeners() {
2996
3001
  this.plane = this.querySelector(".fig-input-joystick-plane");
2997
3002
  this.cursor = this.querySelector(".fig-input-joystick-handle");
2998
- this.xInput = this.querySelector("fig-input-text[name='x']");
2999
- this.yInput = this.querySelector("fig-input-text[name='y']");
3003
+ this.xInput = this.querySelector("fig-input-number[name='x']");
3004
+ this.yInput = this.querySelector("fig-input-number[name='y']");
3000
3005
  this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
3001
3006
  this.plane.addEventListener(
3002
3007
  "touchstart",
@@ -3011,7 +3016,10 @@ class FigInputJoystick extends HTMLElement {
3011
3016
  }
3012
3017
 
3013
3018
  #cleanupListeners() {
3014
- this.plane.removeEventListener("mousedown", this.#handleMouseDown);
3019
+ if (this.plane) {
3020
+ this.plane.removeEventListener("mousedown", this.#handleMouseDown);
3021
+ this.plane.removeEventListener("touchstart", this.#handleTouchStart);
3022
+ }
3015
3023
  window.removeEventListener("keydown", this.#handleKeyDown);
3016
3024
  window.removeEventListener("keyup", this.#handleKeyUp);
3017
3025
  if (this.text && this.xInput && this.yInput) {
@@ -3022,7 +3030,7 @@ class FigInputJoystick extends HTMLElement {
3022
3030
 
3023
3031
  #handleXInput(e) {
3024
3032
  e.stopPropagation();
3025
- this.position.x = Number(e.target.value);
3033
+ this.position.x = Number(e.target.value) / 100; // Convert from percentage to decimal
3026
3034
  this.#syncHandlePosition();
3027
3035
  this.#emitInputEvent();
3028
3036
  this.#emitChangeEvent();
@@ -3030,7 +3038,7 @@ class FigInputJoystick extends HTMLElement {
3030
3038
 
3031
3039
  #handleYInput(e) {
3032
3040
  e.stopPropagation();
3033
- this.position.y = Number(e.target.value);
3041
+ this.position.y = Number(e.target.value) / 100; // Convert from percentage to decimal
3034
3042
  this.#syncHandlePosition();
3035
3043
  this.#emitInputEvent();
3036
3044
  this.#emitChangeEvent();
@@ -3055,7 +3063,13 @@ class FigInputJoystick extends HTMLElement {
3055
3063
  #updatePosition(e) {
3056
3064
  const rect = this.plane.getBoundingClientRect();
3057
3065
  let x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
3058
- let y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
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;
3059
3073
 
3060
3074
  x = this.#snapToGuide(x);
3061
3075
  y = this.#snapToGuide(y);
@@ -3063,11 +3077,12 @@ class FigInputJoystick extends HTMLElement {
3063
3077
  const snapped = this.#snapToDiagonal(x, y);
3064
3078
  this.position = snapped;
3065
3079
 
3080
+ const displayY = this.#displayY(snapped.y);
3066
3081
  this.cursor.style.left = `${snapped.x * 100}%`;
3067
- this.cursor.style.top = `${snapped.y * 100}%`; // Invert Y for display
3082
+ this.cursor.style.top = `${displayY * 100}%`;
3068
3083
  if (this.text && this.xInput && this.yInput) {
3069
- this.xInput.setAttribute("value", snapped.x.toFixed(3));
3070
- this.yInput.setAttribute("value", snapped.y.toFixed(3));
3084
+ this.xInput.setAttribute("value", Math.round(snapped.x * 100));
3085
+ this.yInput.setAttribute("value", Math.round(snapped.y * 100));
3071
3086
  }
3072
3087
 
3073
3088
  this.#emitInputEvent();
@@ -3092,9 +3107,15 @@ class FigInputJoystick extends HTMLElement {
3092
3107
  }
3093
3108
 
3094
3109
  #syncHandlePosition() {
3110
+ const displayY = this.#displayY(this.position.y);
3095
3111
  if (this.cursor) {
3096
3112
  this.cursor.style.left = `${this.position.x * 100}%`;
3097
- this.cursor.style.top = `${this.position.y * 100}%`;
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));
3098
3119
  }
3099
3120
  }
3100
3121
 
@@ -3153,15 +3174,28 @@ class FigInputJoystick extends HTMLElement {
3153
3174
  if (e.key === "Shift") this.isShiftHeld = false;
3154
3175
  }
3155
3176
  static get observedAttributes() {
3156
- return ["value", "precision", "transform", "text"];
3177
+ return ["value", "precision", "transform", "text", "coordinates"];
3157
3178
  }
3158
3179
  get value() {
3159
- return [this.position.x, this.position.y];
3180
+ // Return as percentage values (0-100)
3181
+ return [
3182
+ Math.round(this.position.x * 100),
3183
+ Math.round(this.position.y * 100),
3184
+ ];
3160
3185
  }
3161
3186
  set value(value) {
3162
- let v = value.toString().split(",").map(Number);
3163
- this.position = { x: v[0], y: v[1] };
3164
- this.#syncHandlePosition();
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
+ }
3165
3199
  }
3166
3200
  attributeChangedCallback(name, oldValue, newValue) {
3167
3201
  if (name === "value") {
@@ -3177,6 +3211,10 @@ class FigInputJoystick extends HTMLElement {
3177
3211
  this.text = newValue.toLowerCase() === "true";
3178
3212
  this.#render();
3179
3213
  }
3214
+ if (name === "coordinates") {
3215
+ this.coordinates = newValue || "screen";
3216
+ this.#syncHandlePosition();
3217
+ }
3180
3218
  }
3181
3219
  }
3182
3220
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "devDependencies": {