@rogieking/figui3 2.39.0 → 3.0.1
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/README.md +7 -6
- package/components.css +88 -24
- package/fig.js +197 -65
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -665,24 +665,25 @@ An image display or upload component.
|
|
|
665
665
|
|
|
666
666
|
---
|
|
667
667
|
|
|
668
|
-
###
|
|
668
|
+
### Joystick (`<fig-joystick>`)
|
|
669
669
|
|
|
670
670
|
A 2D position input control.
|
|
671
671
|
|
|
672
672
|
| Attribute | Type | Default | Description |
|
|
673
673
|
|-----------|------|---------|-------------|
|
|
674
|
-
| `value` | string |
|
|
674
|
+
| `value` | string | `"50% 50%"` | Position as percentages (for example `"50% 50%"` or `"25% 80%"`) |
|
|
675
675
|
| `precision` | number | — | Decimal places |
|
|
676
676
|
| `transform` | number | — | Output scaling |
|
|
677
|
-
| `
|
|
677
|
+
| `fields` | boolean | `false` | Show X/Y inputs |
|
|
678
678
|
| `coordinates` | string | `"screen"` | Coordinate system: `"screen"` (0,0 top-left) or `"math"` (0,0 bottom-left) |
|
|
679
|
+
| `aspect-ratio` | string | `"1 / 1"` | Plane container ratio (for example `16 / 9`) |
|
|
679
680
|
|
|
680
681
|
```html
|
|
681
|
-
<fig-
|
|
682
|
-
<fig-
|
|
682
|
+
<fig-joystick value="50% 50%"></fig-joystick>
|
|
683
|
+
<fig-joystick value="50% 50%" fields="true" precision="2"></fig-joystick>
|
|
683
684
|
|
|
684
685
|
<!-- Math coordinates (Y-axis inverted: 0,0 at bottom-left) -->
|
|
685
|
-
<fig-
|
|
686
|
+
<fig-joystick value="50% 50%" coordinates="math" fields="true"></fig-joystick>
|
|
686
687
|
```
|
|
687
688
|
|
|
688
689
|
---
|
package/components.css
CHANGED
|
@@ -1791,7 +1791,8 @@ fig-origin-grid {
|
|
|
1791
1791
|
|
|
1792
1792
|
&.overflow-right {
|
|
1793
1793
|
--origin-overflow-nudge-x: calc(
|
|
1794
|
-
var(--origin-overflow-nudge-base) *
|
|
1794
|
+
var(--origin-overflow-nudge-base) *
|
|
1795
|
+
var(--origin-overflow-nudge-x-scale) * -1
|
|
1795
1796
|
);
|
|
1796
1797
|
}
|
|
1797
1798
|
|
|
@@ -1803,7 +1804,8 @@ fig-origin-grid {
|
|
|
1803
1804
|
|
|
1804
1805
|
&.overflow-down {
|
|
1805
1806
|
--origin-overflow-nudge-y: calc(
|
|
1806
|
-
var(--origin-overflow-nudge-base) *
|
|
1807
|
+
var(--origin-overflow-nudge-base) *
|
|
1808
|
+
var(--origin-overflow-nudge-y-scale) * -1
|
|
1807
1809
|
);
|
|
1808
1810
|
}
|
|
1809
1811
|
}
|
|
@@ -3234,11 +3236,14 @@ fig-segmented-control {
|
|
|
3234
3236
|
}
|
|
3235
3237
|
}
|
|
3236
3238
|
|
|
3237
|
-
fig-
|
|
3238
|
-
--size:
|
|
3239
|
-
|
|
3239
|
+
fig-joystick {
|
|
3240
|
+
--size: 100%;
|
|
3241
|
+
--aspect-ratio: 1 / 1;
|
|
3242
|
+
display: flex;
|
|
3243
|
+
flex-direction: column;
|
|
3240
3244
|
gap: var(--spacer-2);
|
|
3241
3245
|
user-select: none;
|
|
3246
|
+
width: var(--size);
|
|
3242
3247
|
|
|
3243
3248
|
/* When inside horizontal fig-field, grow to fill and let inputs expand */
|
|
3244
3249
|
fig-field[direction="horizontal"] & {
|
|
@@ -3250,10 +3255,21 @@ fig-input-joystick {
|
|
|
3250
3255
|
width: auto;
|
|
3251
3256
|
}
|
|
3252
3257
|
}
|
|
3258
|
+
|
|
3259
|
+
.joystick-values {
|
|
3260
|
+
display: grid;
|
|
3261
|
+
grid-template-columns: 1fr 1fr;
|
|
3262
|
+
gap: var(--spacer-2);
|
|
3263
|
+
}
|
|
3264
|
+
|
|
3265
|
+
.joystick-values > fig-input-number {
|
|
3266
|
+
width: 100%;
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3253
3269
|
.fig-input-joystick-plane-container {
|
|
3254
3270
|
display: flex;
|
|
3255
|
-
width:
|
|
3256
|
-
|
|
3271
|
+
width: 100%;
|
|
3272
|
+
aspect-ratio: var(--aspect-ratio);
|
|
3257
3273
|
place-items: center;
|
|
3258
3274
|
flex-shrink: 0;
|
|
3259
3275
|
flex-grow: 0;
|
|
@@ -3264,22 +3280,61 @@ fig-input-joystick {
|
|
|
3264
3280
|
outline: 0;
|
|
3265
3281
|
}
|
|
3266
3282
|
}
|
|
3283
|
+
|
|
3284
|
+
.fig-joystick-axis-label {
|
|
3285
|
+
position: absolute;
|
|
3286
|
+
z-index: 1;
|
|
3287
|
+
pointer-events: none;
|
|
3288
|
+
user-select: none;
|
|
3289
|
+
color: var(--figma-color-text-tertiary);
|
|
3290
|
+
font-size: 10px;
|
|
3291
|
+
line-height: 1;
|
|
3292
|
+
letter-spacing: 0.01em;
|
|
3293
|
+
font-weight: 700;
|
|
3294
|
+
text-shadow:
|
|
3295
|
+
1px 0 0 var(--figma-color-bg-secondary),
|
|
3296
|
+
-1px 0 0 var(--figma-color-bg-secondary),
|
|
3297
|
+
0 1px 0 var(--figma-color-bg-secondary),
|
|
3298
|
+
0 -1px 0 var(--figma-color-bg-secondary);
|
|
3299
|
+
|
|
3300
|
+
&.top {
|
|
3301
|
+
top: var(--spacer-2);
|
|
3302
|
+
left: 50%;
|
|
3303
|
+
transform: translateX(-50%);
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
&.bottom {
|
|
3307
|
+
bottom: var(--spacer-2);
|
|
3308
|
+
left: 50%;
|
|
3309
|
+
transform: translateX(-50%);
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
&.left {
|
|
3313
|
+
left: var(--spacer-2);
|
|
3314
|
+
top: 50%;
|
|
3315
|
+
transform: translateY(-50%) rotate(-90deg);
|
|
3316
|
+
transform-origin: center;
|
|
3317
|
+
|
|
3318
|
+
&.no-rotate {
|
|
3319
|
+
transform: translateY(-50%);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
&.right {
|
|
3324
|
+
right: var(--spacer-2);
|
|
3325
|
+
top: 50%;
|
|
3326
|
+
transform: translateY(-50%) rotate(90deg);
|
|
3327
|
+
transform-origin: center;
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3267
3331
|
.fig-input-joystick-plane {
|
|
3268
3332
|
display: inline-grid;
|
|
3269
3333
|
place-items: center;
|
|
3270
3334
|
position: relative;
|
|
3271
|
-
width:
|
|
3272
|
-
height:
|
|
3335
|
+
width: 100%;
|
|
3336
|
+
height: 100%;
|
|
3273
3337
|
flex-shrink: 0;
|
|
3274
|
-
&.dragging {
|
|
3275
|
-
cursor: grab;
|
|
3276
|
-
--size: 3rem;
|
|
3277
|
-
z-index: 2;
|
|
3278
|
-
position: absolute;
|
|
3279
|
-
top: 50%;
|
|
3280
|
-
left: 50%;
|
|
3281
|
-
transform: translate(-50%, -50%);
|
|
3282
|
-
}
|
|
3283
3338
|
}
|
|
3284
3339
|
.fig-input-joystick-plane > * {
|
|
3285
3340
|
grid-area: 1/1;
|
|
@@ -3288,7 +3343,6 @@ fig-input-joystick {
|
|
|
3288
3343
|
position: absolute;
|
|
3289
3344
|
width: var(--size);
|
|
3290
3345
|
height: var(--size);
|
|
3291
|
-
box-shadow: inset 0 0 0 1px var(--figma-color-border);
|
|
3292
3346
|
border-radius: var(--radius-medium);
|
|
3293
3347
|
overflow: hidden;
|
|
3294
3348
|
background: var(--figma-color-bg-secondary);
|
|
@@ -3339,13 +3393,23 @@ fig-input-joystick {
|
|
|
3339
3393
|
|
|
3340
3394
|
.fig-input-joystick-handle {
|
|
3341
3395
|
position: absolute;
|
|
3342
|
-
z-index:
|
|
3343
|
-
width:
|
|
3344
|
-
height:
|
|
3345
|
-
background: var(--
|
|
3346
|
-
box-shadow:
|
|
3396
|
+
z-index: 2;
|
|
3397
|
+
width: 1rem;
|
|
3398
|
+
height: 1rem;
|
|
3399
|
+
background: var(--figma-color-bg-brand);
|
|
3400
|
+
box-shadow:
|
|
3401
|
+
inset 0 0 0 0.125rem var(--handle-color),
|
|
3402
|
+
0px 0 0 0.5px rgba(0, 0, 0, 0.1),
|
|
3403
|
+
var(--figma-elevation-200);
|
|
3347
3404
|
border-radius: 50%;
|
|
3348
3405
|
transform: translate(-50%, -50%);
|
|
3406
|
+
transition: box-shadow var(--slider-transition);
|
|
3407
|
+
&:hover {
|
|
3408
|
+
box-shadow:
|
|
3409
|
+
inset 0 0 0 0.25rem var(--handle-color),
|
|
3410
|
+
0px 0 0 0.5px rgba(0, 0, 0, 0.1),
|
|
3411
|
+
var(--figma-elevation-200);
|
|
3412
|
+
}
|
|
3349
3413
|
}
|
|
3350
3414
|
}
|
|
3351
3415
|
|
package/fig.js
CHANGED
|
@@ -7495,12 +7495,24 @@ customElements.define("fig-origin-grid", FigOriginGrid);
|
|
|
7495
7495
|
|
|
7496
7496
|
/**
|
|
7497
7497
|
* A custom joystick input element.
|
|
7498
|
-
* @attr {string} value - The current position of the joystick (e.g., "
|
|
7498
|
+
* @attr {string} value - The current position of the joystick (e.g., "50% 50%").
|
|
7499
7499
|
* @attr {number} precision - The number of decimal places for the output.
|
|
7500
7500
|
* @attr {number} transform - A scaling factor for the output.
|
|
7501
|
-
* @attr {boolean}
|
|
7501
|
+
* @attr {boolean} fields - Whether to display X and Y inputs.
|
|
7502
|
+
* @attr {string} aspect-ratio - Aspect ratio for the joystick plane container.
|
|
7503
|
+
* @attr {string} axis-labels - Space-delimited labels. 1 token: top. 2 tokens: x y. 4 tokens: left right top bottom.
|
|
7502
7504
|
*/
|
|
7503
7505
|
class FigInputJoystick extends HTMLElement {
|
|
7506
|
+
#boundMouseDown = null;
|
|
7507
|
+
#boundTouchStart = null;
|
|
7508
|
+
#boundKeyDown = null;
|
|
7509
|
+
#boundKeyUp = null;
|
|
7510
|
+
#boundXInput = null;
|
|
7511
|
+
#boundYInput = null;
|
|
7512
|
+
#boundXFocusOut = null;
|
|
7513
|
+
#boundYFocusOut = null;
|
|
7514
|
+
#isSyncingValueAttr = false;
|
|
7515
|
+
|
|
7504
7516
|
constructor() {
|
|
7505
7517
|
super();
|
|
7506
7518
|
|
|
@@ -7513,6 +7525,14 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7513
7525
|
this.yInput = null;
|
|
7514
7526
|
this.coordinates = "screen"; // "screen" (0,0 top-left) or "math" (0,0 bottom-left)
|
|
7515
7527
|
this.#initialized = false;
|
|
7528
|
+
this.#boundMouseDown = (e) => this.#handleMouseDown(e);
|
|
7529
|
+
this.#boundTouchStart = (e) => this.#handleTouchStart(e);
|
|
7530
|
+
this.#boundKeyDown = (e) => this.#handleKeyDown(e);
|
|
7531
|
+
this.#boundKeyUp = (e) => this.#handleKeyUp(e);
|
|
7532
|
+
this.#boundXInput = (e) => this.#handleXInput(e);
|
|
7533
|
+
this.#boundYInput = (e) => this.#handleYInput(e);
|
|
7534
|
+
this.#boundXFocusOut = () => this.#handleFieldFocusOut();
|
|
7535
|
+
this.#boundYFocusOut = () => this.#handleFieldFocusOut();
|
|
7516
7536
|
}
|
|
7517
7537
|
|
|
7518
7538
|
#initialized = false;
|
|
@@ -7524,12 +7544,16 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7524
7544
|
this.precision = parseInt(this.precision);
|
|
7525
7545
|
this.transform = this.getAttribute("transform") || 1;
|
|
7526
7546
|
this.transform = Number(this.transform);
|
|
7527
|
-
this.text = this.getAttribute("text") === "true";
|
|
7528
7547
|
this.coordinates = this.getAttribute("coordinates") || "screen";
|
|
7548
|
+
this.#syncAspectRatioVar(this.getAttribute("aspect-ratio"));
|
|
7549
|
+
if (!this.hasAttribute("value")) {
|
|
7550
|
+
this.setAttribute("value", "50% 50%");
|
|
7551
|
+
}
|
|
7529
7552
|
|
|
7530
7553
|
this.#render();
|
|
7531
7554
|
this.#setupListeners();
|
|
7532
7555
|
this.#syncHandlePosition();
|
|
7556
|
+
this.#syncValueAttribute();
|
|
7533
7557
|
this.#initialized = true;
|
|
7534
7558
|
});
|
|
7535
7559
|
}
|
|
@@ -7538,41 +7562,102 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7538
7562
|
#displayY(y) {
|
|
7539
7563
|
return this.coordinates === "math" ? 1 - y : y;
|
|
7540
7564
|
}
|
|
7565
|
+
|
|
7566
|
+
#syncAspectRatioVar(value) {
|
|
7567
|
+
if (value && value.trim()) {
|
|
7568
|
+
this.style.setProperty("--aspect-ratio", value.trim());
|
|
7569
|
+
} else {
|
|
7570
|
+
this.style.removeProperty("--aspect-ratio");
|
|
7571
|
+
}
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7541
7574
|
disconnectedCallback() {
|
|
7542
7575
|
this.#cleanupListeners();
|
|
7543
7576
|
}
|
|
7544
7577
|
|
|
7578
|
+
get #fieldsEnabled() {
|
|
7579
|
+
const fields = this.getAttribute("fields");
|
|
7580
|
+
if (fields === null) return false;
|
|
7581
|
+
return fields.toLowerCase() !== "false";
|
|
7582
|
+
}
|
|
7583
|
+
|
|
7545
7584
|
#render() {
|
|
7546
7585
|
this.innerHTML = this.#getInnerHTML();
|
|
7547
7586
|
}
|
|
7587
|
+
|
|
7588
|
+
#getAxisLabels() {
|
|
7589
|
+
const raw = (this.getAttribute("axis-labels") || "").trim();
|
|
7590
|
+
if (!raw) {
|
|
7591
|
+
return { left: "", right: "", top: "", bottom: "", leftNoRotate: false };
|
|
7592
|
+
}
|
|
7593
|
+
const tokens = raw.split(/\s+/).filter(Boolean);
|
|
7594
|
+
if (tokens.length === 1) {
|
|
7595
|
+
return {
|
|
7596
|
+
left: "",
|
|
7597
|
+
right: "",
|
|
7598
|
+
top: tokens[0],
|
|
7599
|
+
bottom: "",
|
|
7600
|
+
leftNoRotate: false,
|
|
7601
|
+
};
|
|
7602
|
+
}
|
|
7603
|
+
if (tokens.length === 2) {
|
|
7604
|
+
const [x, y] = tokens;
|
|
7605
|
+
return { left: x, right: "", top: "", bottom: y, leftNoRotate: true };
|
|
7606
|
+
}
|
|
7607
|
+
if (tokens.length === 4) {
|
|
7608
|
+
const [left, right, top, bottom] = tokens;
|
|
7609
|
+
return { left, right, top, bottom, leftNoRotate: false };
|
|
7610
|
+
}
|
|
7611
|
+
return { left: "", right: "", top: "", bottom: "", leftNoRotate: false };
|
|
7612
|
+
}
|
|
7613
|
+
|
|
7548
7614
|
#getInnerHTML() {
|
|
7615
|
+
const axisLabels = this.#getAxisLabels();
|
|
7616
|
+
const labelsMarkup = [
|
|
7617
|
+
axisLabels.left
|
|
7618
|
+
? `<label class="fig-joystick-axis-label left${axisLabels.leftNoRotate ? " no-rotate" : ""}" aria-hidden="true">${axisLabels.left}</label>`
|
|
7619
|
+
: "",
|
|
7620
|
+
axisLabels.right
|
|
7621
|
+
? `<label class="fig-joystick-axis-label right" aria-hidden="true">${axisLabels.right}</label>`
|
|
7622
|
+
: "",
|
|
7623
|
+
axisLabels.top
|
|
7624
|
+
? `<label class="fig-joystick-axis-label top" aria-hidden="true">${axisLabels.top}</label>`
|
|
7625
|
+
: "",
|
|
7626
|
+
axisLabels.bottom
|
|
7627
|
+
? `<label class="fig-joystick-axis-label bottom" aria-hidden="true">${axisLabels.bottom}</label>`
|
|
7628
|
+
: "",
|
|
7629
|
+
].join("");
|
|
7630
|
+
|
|
7549
7631
|
return `
|
|
7550
7632
|
<div class="fig-input-joystick-plane-container" tabindex="0">
|
|
7633
|
+
${labelsMarkup}
|
|
7551
7634
|
<div class="fig-input-joystick-plane">
|
|
7552
7635
|
<div class="fig-input-joystick-guides"></div>
|
|
7553
7636
|
<div class="fig-input-joystick-handle"></div>
|
|
7554
7637
|
</div>
|
|
7555
7638
|
</div>
|
|
7556
7639
|
${
|
|
7557
|
-
this
|
|
7558
|
-
? `<
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7640
|
+
this.#fieldsEnabled
|
|
7641
|
+
? `<div class="joystick-values">
|
|
7642
|
+
<fig-input-number
|
|
7643
|
+
name="x"
|
|
7644
|
+
step="1"
|
|
7645
|
+
value="${(this.position.x * 100).toFixed(this.precision)}"
|
|
7646
|
+
min="0"
|
|
7647
|
+
max="100"
|
|
7648
|
+
units="%">
|
|
7649
|
+
<span slot="prepend">X</span>
|
|
7650
|
+
</fig-input-number>
|
|
7651
|
+
<fig-input-number
|
|
7652
|
+
name="y"
|
|
7653
|
+
step="1"
|
|
7654
|
+
min="0"
|
|
7655
|
+
max="100"
|
|
7656
|
+
value="${(this.position.y * 100).toFixed(this.precision)}"
|
|
7657
|
+
units="%">
|
|
7658
|
+
<span slot="prepend">Y</span>
|
|
7659
|
+
</fig-input-number>
|
|
7660
|
+
</div>`
|
|
7576
7661
|
: ""
|
|
7577
7662
|
}
|
|
7578
7663
|
`;
|
|
@@ -7583,45 +7668,57 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7583
7668
|
this.cursor = this.querySelector(".fig-input-joystick-handle");
|
|
7584
7669
|
this.xInput = this.querySelector("fig-input-number[name='x']");
|
|
7585
7670
|
this.yInput = this.querySelector("fig-input-number[name='y']");
|
|
7586
|
-
this.plane.addEventListener("mousedown", this.#
|
|
7587
|
-
this.plane.addEventListener(
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
)
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
this.
|
|
7595
|
-
this.yInput.addEventListener("
|
|
7671
|
+
this.plane.addEventListener("mousedown", this.#boundMouseDown);
|
|
7672
|
+
this.plane.addEventListener("touchstart", this.#boundTouchStart);
|
|
7673
|
+
window.addEventListener("keydown", this.#boundKeyDown);
|
|
7674
|
+
window.addEventListener("keyup", this.#boundKeyUp);
|
|
7675
|
+
if (this.#fieldsEnabled && this.xInput && this.yInput) {
|
|
7676
|
+
this.xInput.addEventListener("input", this.#boundXInput);
|
|
7677
|
+
this.xInput.addEventListener("change", this.#boundXInput);
|
|
7678
|
+
this.xInput.addEventListener("focusout", this.#boundXFocusOut);
|
|
7679
|
+
this.yInput.addEventListener("input", this.#boundYInput);
|
|
7680
|
+
this.yInput.addEventListener("change", this.#boundYInput);
|
|
7681
|
+
this.yInput.addEventListener("focusout", this.#boundYFocusOut);
|
|
7596
7682
|
}
|
|
7597
7683
|
}
|
|
7598
7684
|
|
|
7599
7685
|
#cleanupListeners() {
|
|
7600
7686
|
if (this.plane) {
|
|
7601
|
-
this.plane.removeEventListener("mousedown", this.#
|
|
7602
|
-
this.plane.removeEventListener("touchstart", this.#
|
|
7687
|
+
this.plane.removeEventListener("mousedown", this.#boundMouseDown);
|
|
7688
|
+
this.plane.removeEventListener("touchstart", this.#boundTouchStart);
|
|
7603
7689
|
}
|
|
7604
|
-
window.removeEventListener("keydown", this.#
|
|
7605
|
-
window.removeEventListener("keyup", this.#
|
|
7606
|
-
if (this
|
|
7607
|
-
this.xInput.removeEventListener("input", this.#
|
|
7608
|
-
this.
|
|
7690
|
+
window.removeEventListener("keydown", this.#boundKeyDown);
|
|
7691
|
+
window.removeEventListener("keyup", this.#boundKeyUp);
|
|
7692
|
+
if (this.#fieldsEnabled && this.xInput && this.yInput) {
|
|
7693
|
+
this.xInput.removeEventListener("input", this.#boundXInput);
|
|
7694
|
+
this.xInput.removeEventListener("change", this.#boundXInput);
|
|
7695
|
+
this.xInput.removeEventListener("focusout", this.#boundXFocusOut);
|
|
7696
|
+
this.yInput.removeEventListener("input", this.#boundYInput);
|
|
7697
|
+
this.yInput.removeEventListener("change", this.#boundYInput);
|
|
7698
|
+
this.yInput.removeEventListener("focusout", this.#boundYFocusOut);
|
|
7609
7699
|
}
|
|
7610
7700
|
}
|
|
7611
7701
|
|
|
7612
7702
|
#handleXInput(e) {
|
|
7613
|
-
e.
|
|
7614
|
-
|
|
7703
|
+
const next = Number.parseFloat(e.target.value);
|
|
7704
|
+
if (!Number.isFinite(next)) return;
|
|
7705
|
+
this.position.x = Math.max(0, Math.min(1, next / 100));
|
|
7615
7706
|
this.#syncHandlePosition();
|
|
7707
|
+
this.#syncValueAttribute();
|
|
7616
7708
|
this.#emitInputEvent();
|
|
7617
|
-
this.#emitChangeEvent();
|
|
7618
7709
|
}
|
|
7619
7710
|
|
|
7620
7711
|
#handleYInput(e) {
|
|
7621
|
-
e.
|
|
7622
|
-
|
|
7712
|
+
const next = Number.parseFloat(e.target.value);
|
|
7713
|
+
if (!Number.isFinite(next)) return;
|
|
7714
|
+
this.position.y = Math.max(0, Math.min(1, next / 100));
|
|
7623
7715
|
this.#syncHandlePosition();
|
|
7716
|
+
this.#syncValueAttribute();
|
|
7624
7717
|
this.#emitInputEvent();
|
|
7718
|
+
}
|
|
7719
|
+
|
|
7720
|
+
#handleFieldFocusOut() {
|
|
7721
|
+
this.#syncValueAttribute();
|
|
7625
7722
|
this.#emitChangeEvent();
|
|
7626
7723
|
}
|
|
7627
7724
|
|
|
@@ -7661,11 +7758,12 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7661
7758
|
const displayY = this.#displayY(snapped.y);
|
|
7662
7759
|
this.cursor.style.left = `${snapped.x * 100}%`;
|
|
7663
7760
|
this.cursor.style.top = `${displayY * 100}%`;
|
|
7664
|
-
if (this
|
|
7761
|
+
if (this.#fieldsEnabled && this.xInput && this.yInput) {
|
|
7665
7762
|
this.xInput.setAttribute("value", Math.round(snapped.x * 100));
|
|
7666
7763
|
this.yInput.setAttribute("value", Math.round(snapped.y * 100));
|
|
7667
7764
|
}
|
|
7668
7765
|
|
|
7766
|
+
this.#syncValueAttribute();
|
|
7669
7767
|
this.#emitInputEvent();
|
|
7670
7768
|
}
|
|
7671
7769
|
|
|
@@ -7694,12 +7792,20 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7694
7792
|
this.cursor.style.top = `${displayY * 100}%`;
|
|
7695
7793
|
}
|
|
7696
7794
|
// Also sync text inputs if they exist (convert to percentage 0-100)
|
|
7697
|
-
if (this
|
|
7795
|
+
if (this.#fieldsEnabled && this.xInput && this.yInput) {
|
|
7698
7796
|
this.xInput.setAttribute("value", Math.round(this.position.x * 100));
|
|
7699
7797
|
this.yInput.setAttribute("value", Math.round(this.position.y * 100));
|
|
7700
7798
|
}
|
|
7701
7799
|
}
|
|
7702
7800
|
|
|
7801
|
+
#syncValueAttribute() {
|
|
7802
|
+
const next = this.value;
|
|
7803
|
+
if (this.getAttribute("value") === next) return;
|
|
7804
|
+
this.#isSyncingValueAttr = true;
|
|
7805
|
+
this.setAttribute("value", next);
|
|
7806
|
+
this.#isSyncingValueAttr = false;
|
|
7807
|
+
}
|
|
7808
|
+
|
|
7703
7809
|
#handleMouseDown(e) {
|
|
7704
7810
|
this.isDragging = true;
|
|
7705
7811
|
|
|
@@ -7718,6 +7824,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7718
7824
|
this.plane.style.cursor = "";
|
|
7719
7825
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
7720
7826
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
7827
|
+
this.#syncValueAttribute();
|
|
7721
7828
|
this.#emitChangeEvent();
|
|
7722
7829
|
};
|
|
7723
7830
|
|
|
@@ -7740,6 +7847,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7740
7847
|
this.plane.classList.remove("dragging");
|
|
7741
7848
|
window.removeEventListener("touchmove", handleTouchMove);
|
|
7742
7849
|
window.removeEventListener("touchend", handleTouchEnd);
|
|
7850
|
+
this.#syncValueAttribute();
|
|
7743
7851
|
this.#emitChangeEvent();
|
|
7744
7852
|
};
|
|
7745
7853
|
|
|
@@ -7759,32 +7867,48 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7759
7867
|
container?.focus();
|
|
7760
7868
|
}
|
|
7761
7869
|
static get observedAttributes() {
|
|
7762
|
-
return ["value", "precision", "transform", "text", "coordinates"];
|
|
7763
|
-
}
|
|
7764
|
-
get value() {
|
|
7765
|
-
// Return as percentage values (0-100)
|
|
7766
7870
|
return [
|
|
7767
|
-
|
|
7768
|
-
|
|
7871
|
+
"value",
|
|
7872
|
+
"precision",
|
|
7873
|
+
"transform",
|
|
7874
|
+
"fields",
|
|
7875
|
+
"coordinates",
|
|
7876
|
+
"aspect-ratio",
|
|
7877
|
+
"axis-labels",
|
|
7769
7878
|
];
|
|
7770
7879
|
}
|
|
7880
|
+
get value() {
|
|
7881
|
+
return `${Math.round(this.position.x * 100)}% ${Math.round(this.position.y * 100)}%`;
|
|
7882
|
+
}
|
|
7771
7883
|
set value(value) {
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
.
|
|
7775
|
-
|
|
7776
|
-
.
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7884
|
+
const normalized = value == null ? "" : String(value).trim();
|
|
7885
|
+
if (!normalized) {
|
|
7886
|
+
this.position = { x: 0.5, y: 0.5 };
|
|
7887
|
+
} else {
|
|
7888
|
+
const parts = normalized.split(/[\s,]+/).filter(Boolean);
|
|
7889
|
+
const parseAxis = (token) => {
|
|
7890
|
+
if (!token) return 0.5;
|
|
7891
|
+
const isPercent = token.includes("%");
|
|
7892
|
+
const numeric = Number.parseFloat(token.replace(/%/g, "").trim());
|
|
7893
|
+
if (!Number.isFinite(numeric)) return 0.5;
|
|
7894
|
+
const decimal = isPercent || Math.abs(numeric) > 1 ? numeric / 100 : numeric;
|
|
7895
|
+
return Math.max(0, Math.min(1, decimal));
|
|
7896
|
+
};
|
|
7897
|
+
const x = parseAxis(parts[0]);
|
|
7898
|
+
const y = parseAxis(parts[1] ?? parts[0]);
|
|
7899
|
+
this.position = { x, y };
|
|
7900
|
+
}
|
|
7781
7901
|
if (this.#initialized) {
|
|
7782
7902
|
this.#syncHandlePosition();
|
|
7783
7903
|
}
|
|
7784
7904
|
}
|
|
7785
7905
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
7906
|
+
if (name === "aspect-ratio") {
|
|
7907
|
+
this.#syncAspectRatioVar(newValue);
|
|
7908
|
+
return;
|
|
7909
|
+
}
|
|
7786
7910
|
if (name === "value") {
|
|
7787
|
-
if (this.isDragging) return;
|
|
7911
|
+
if (this.#isSyncingValueAttr || this.isDragging) return;
|
|
7788
7912
|
this.value = newValue;
|
|
7789
7913
|
}
|
|
7790
7914
|
if (name === "precision") {
|
|
@@ -7793,9 +7917,17 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7793
7917
|
if (name === "transform") {
|
|
7794
7918
|
this.transform = Number(newValue);
|
|
7795
7919
|
}
|
|
7796
|
-
if (name === "
|
|
7797
|
-
this
|
|
7920
|
+
if (name === "fields" && newValue !== oldValue) {
|
|
7921
|
+
this.#cleanupListeners();
|
|
7922
|
+
this.#render();
|
|
7923
|
+
this.#setupListeners();
|
|
7924
|
+
this.#syncHandlePosition();
|
|
7925
|
+
}
|
|
7926
|
+
if (name === "axis-labels" && newValue !== oldValue) {
|
|
7927
|
+
this.#cleanupListeners();
|
|
7798
7928
|
this.#render();
|
|
7929
|
+
this.#setupListeners();
|
|
7930
|
+
this.#syncHandlePosition();
|
|
7799
7931
|
}
|
|
7800
7932
|
if (name === "coordinates") {
|
|
7801
7933
|
this.coordinates = newValue || "screen";
|
|
@@ -7804,7 +7936,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
7804
7936
|
}
|
|
7805
7937
|
}
|
|
7806
7938
|
|
|
7807
|
-
customElements.define("fig-
|
|
7939
|
+
customElements.define("fig-joystick", FigInputJoystick);
|
|
7808
7940
|
|
|
7809
7941
|
/**
|
|
7810
7942
|
* A custom angle chooser input element.
|
package/package.json
CHANGED