@rogieking/figui3 2.0.2 → 2.0.4
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 +115 -18
- package/example.html +334 -241
- package/fig.js +156 -46
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -5,6 +5,25 @@
|
|
|
5
5
|
function figUniqueId() {
|
|
6
6
|
return Date.now().toString(36) + Math.random().toString(36).substring(2);
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Gets the highest z-index currently in use on the page
|
|
11
|
+
* @returns {number} The highest z-index found, minimum of 1000
|
|
12
|
+
*/
|
|
13
|
+
function figGetHighestZIndex() {
|
|
14
|
+
let highest = 1000; // Baseline minimum
|
|
15
|
+
|
|
16
|
+
// Check all elements with inline z-index or computed z-index
|
|
17
|
+
const elements = document.querySelectorAll("*");
|
|
18
|
+
for (const el of elements) {
|
|
19
|
+
const zIndex = parseInt(getComputedStyle(el).zIndex, 10);
|
|
20
|
+
if (!isNaN(zIndex) && zIndex > highest) {
|
|
21
|
+
highest = zIndex;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return highest;
|
|
26
|
+
}
|
|
8
27
|
/**
|
|
9
28
|
* Checks if the browser supports the native popover API
|
|
10
29
|
* @returns {boolean} True if popover is supported
|
|
@@ -326,7 +345,15 @@ class FigTooltip extends HTMLElement {
|
|
|
326
345
|
this.popup.style.pointerEvents = "none";
|
|
327
346
|
this.popup.append(content);
|
|
328
347
|
content.innerText = this.getAttribute("text");
|
|
329
|
-
|
|
348
|
+
|
|
349
|
+
// If tooltip is inside a dialog, append to dialog to stay in top layer
|
|
350
|
+
const parentDialog = this.closest("dialog");
|
|
351
|
+
if (parentDialog && parentDialog.open) {
|
|
352
|
+
parentDialog.append(this.popup);
|
|
353
|
+
} else {
|
|
354
|
+
document.body.append(this.popup);
|
|
355
|
+
}
|
|
356
|
+
|
|
330
357
|
const text = content.childNodes[0];
|
|
331
358
|
if (text) {
|
|
332
359
|
const range = document.createRange();
|
|
@@ -451,7 +478,7 @@ class FigTooltip extends HTMLElement {
|
|
|
451
478
|
this.popup.style.visibility = "visible";
|
|
452
479
|
this.popup.style.display = "block";
|
|
453
480
|
this.popup.style.pointerEvents = "all";
|
|
454
|
-
this.popup.style.zIndex =
|
|
481
|
+
this.popup.style.zIndex = figGetHighestZIndex() + 1;
|
|
455
482
|
|
|
456
483
|
this.isOpen = true;
|
|
457
484
|
}
|
|
@@ -2635,10 +2662,15 @@ class FigComboInput extends HTMLElement {
|
|
|
2635
2662
|
}
|
|
2636
2663
|
if (name === "placeholder") {
|
|
2637
2664
|
this.placeholder = newValue;
|
|
2665
|
+
if (this.input) {
|
|
2666
|
+
this.input.setAttribute("placeholder", newValue);
|
|
2667
|
+
}
|
|
2638
2668
|
}
|
|
2639
2669
|
if (name === "value") {
|
|
2640
2670
|
this.value = newValue;
|
|
2641
|
-
this.input
|
|
2671
|
+
if (this.input) {
|
|
2672
|
+
this.input.setAttribute("value", newValue);
|
|
2673
|
+
}
|
|
2642
2674
|
}
|
|
2643
2675
|
}
|
|
2644
2676
|
}
|
|
@@ -2690,6 +2722,9 @@ class FigChit extends HTMLElement {
|
|
|
2690
2722
|
this.#src = value;
|
|
2691
2723
|
this.setAttribute("src", value);
|
|
2692
2724
|
}
|
|
2725
|
+
focus() {
|
|
2726
|
+
this.input?.focus();
|
|
2727
|
+
}
|
|
2693
2728
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
2694
2729
|
switch (name) {
|
|
2695
2730
|
case "src":
|
|
@@ -2918,15 +2953,20 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2918
2953
|
constructor() {
|
|
2919
2954
|
super();
|
|
2920
2955
|
|
|
2921
|
-
this.position = { x: 0.5, y: 0.5 };
|
|
2922
|
-
this.value = [0.5, 0.5];
|
|
2956
|
+
this.position = { x: 0.5, y: 0.5 }; // Internal position (0-1)
|
|
2923
2957
|
this.isDragging = false;
|
|
2924
2958
|
this.isShiftHeld = false;
|
|
2925
2959
|
this.plane = null;
|
|
2926
2960
|
this.cursor = null;
|
|
2927
2961
|
this.xInput = null;
|
|
2928
2962
|
this.yInput = null;
|
|
2963
|
+
this.coordinates = "screen"; // "screen" (0,0 top-left) or "math" (0,0 bottom-left)
|
|
2964
|
+
this.#initialized = false;
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
#initialized = false;
|
|
2929
2968
|
|
|
2969
|
+
connectedCallback() {
|
|
2930
2970
|
// Initialize position
|
|
2931
2971
|
requestAnimationFrame(() => {
|
|
2932
2972
|
this.precision = this.getAttribute("precision") || 3;
|
|
@@ -2934,24 +2974,19 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2934
2974
|
this.transform = this.getAttribute("transform") || 1;
|
|
2935
2975
|
this.transform = Number(this.transform);
|
|
2936
2976
|
this.text = this.getAttribute("text") === "true";
|
|
2977
|
+
this.coordinates = this.getAttribute("coordinates") || "screen";
|
|
2937
2978
|
|
|
2938
2979
|
this.#render();
|
|
2939
|
-
|
|
2940
2980
|
this.#setupListeners();
|
|
2941
|
-
|
|
2942
2981
|
this.#syncHandlePosition();
|
|
2943
|
-
|
|
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
|
-
}
|
|
2982
|
+
this.#initialized = true;
|
|
2953
2983
|
});
|
|
2954
2984
|
}
|
|
2985
|
+
|
|
2986
|
+
// Convert Y for display (CSS uses top-down, math uses bottom-up)
|
|
2987
|
+
#displayY(y) {
|
|
2988
|
+
return this.coordinates === "math" ? 1 - y : y;
|
|
2989
|
+
}
|
|
2955
2990
|
disconnectedCallback() {
|
|
2956
2991
|
this.#cleanupListeners();
|
|
2957
2992
|
}
|
|
@@ -2969,24 +3004,24 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2969
3004
|
</div>
|
|
2970
3005
|
${
|
|
2971
3006
|
this.text
|
|
2972
|
-
? `<fig-input-
|
|
2973
|
-
type="number"
|
|
3007
|
+
? `<fig-input-number
|
|
2974
3008
|
name="x"
|
|
2975
|
-
step="
|
|
2976
|
-
value="${this.position.x}"
|
|
3009
|
+
step="1"
|
|
3010
|
+
value="${this.position.x * 100}"
|
|
2977
3011
|
min="0"
|
|
2978
|
-
max="
|
|
3012
|
+
max="100"
|
|
3013
|
+
units="%">
|
|
2979
3014
|
<span slot="prepend">X</span>
|
|
2980
|
-
</fig-input-
|
|
2981
|
-
<fig-input-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3015
|
+
</fig-input-number>
|
|
3016
|
+
<fig-input-number
|
|
3017
|
+
name="y"
|
|
3018
|
+
step="1"
|
|
3019
|
+
min="0"
|
|
3020
|
+
max="100"
|
|
3021
|
+
value="${this.position.y * 100}"
|
|
3022
|
+
units="%">
|
|
2988
3023
|
<span slot="prepend">Y</span>
|
|
2989
|
-
</fig-input-
|
|
3024
|
+
</fig-input-number>`
|
|
2990
3025
|
: ""
|
|
2991
3026
|
}
|
|
2992
3027
|
`;
|
|
@@ -2995,8 +3030,8 @@ class FigInputJoystick extends HTMLElement {
|
|
|
2995
3030
|
#setupListeners() {
|
|
2996
3031
|
this.plane = this.querySelector(".fig-input-joystick-plane");
|
|
2997
3032
|
this.cursor = this.querySelector(".fig-input-joystick-handle");
|
|
2998
|
-
this.xInput = this.querySelector("fig-input-
|
|
2999
|
-
this.yInput = this.querySelector("fig-input-
|
|
3033
|
+
this.xInput = this.querySelector("fig-input-number[name='x']");
|
|
3034
|
+
this.yInput = this.querySelector("fig-input-number[name='y']");
|
|
3000
3035
|
this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
|
|
3001
3036
|
this.plane.addEventListener(
|
|
3002
3037
|
"touchstart",
|
|
@@ -3011,7 +3046,10 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3011
3046
|
}
|
|
3012
3047
|
|
|
3013
3048
|
#cleanupListeners() {
|
|
3014
|
-
this.plane
|
|
3049
|
+
if (this.plane) {
|
|
3050
|
+
this.plane.removeEventListener("mousedown", this.#handleMouseDown);
|
|
3051
|
+
this.plane.removeEventListener("touchstart", this.#handleTouchStart);
|
|
3052
|
+
}
|
|
3015
3053
|
window.removeEventListener("keydown", this.#handleKeyDown);
|
|
3016
3054
|
window.removeEventListener("keyup", this.#handleKeyUp);
|
|
3017
3055
|
if (this.text && this.xInput && this.yInput) {
|
|
@@ -3022,7 +3060,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3022
3060
|
|
|
3023
3061
|
#handleXInput(e) {
|
|
3024
3062
|
e.stopPropagation();
|
|
3025
|
-
this.position.x = Number(e.target.value);
|
|
3063
|
+
this.position.x = Number(e.target.value) / 100; // Convert from percentage to decimal
|
|
3026
3064
|
this.#syncHandlePosition();
|
|
3027
3065
|
this.#emitInputEvent();
|
|
3028
3066
|
this.#emitChangeEvent();
|
|
@@ -3030,7 +3068,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3030
3068
|
|
|
3031
3069
|
#handleYInput(e) {
|
|
3032
3070
|
e.stopPropagation();
|
|
3033
|
-
this.position.y = Number(e.target.value);
|
|
3071
|
+
this.position.y = Number(e.target.value) / 100; // Convert from percentage to decimal
|
|
3034
3072
|
this.#syncHandlePosition();
|
|
3035
3073
|
this.#emitInputEvent();
|
|
3036
3074
|
this.#emitChangeEvent();
|
|
@@ -3055,7 +3093,13 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3055
3093
|
#updatePosition(e) {
|
|
3056
3094
|
const rect = this.plane.getBoundingClientRect();
|
|
3057
3095
|
let x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
|
3058
|
-
let
|
|
3096
|
+
let screenY = Math.max(
|
|
3097
|
+
0,
|
|
3098
|
+
Math.min(1, (e.clientY - rect.top) / rect.height)
|
|
3099
|
+
);
|
|
3100
|
+
|
|
3101
|
+
// Convert screen Y to internal Y (flip for math coordinates)
|
|
3102
|
+
let y = this.coordinates === "math" ? 1 - screenY : screenY;
|
|
3059
3103
|
|
|
3060
3104
|
x = this.#snapToGuide(x);
|
|
3061
3105
|
y = this.#snapToGuide(y);
|
|
@@ -3063,11 +3107,12 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3063
3107
|
const snapped = this.#snapToDiagonal(x, y);
|
|
3064
3108
|
this.position = snapped;
|
|
3065
3109
|
|
|
3110
|
+
const displayY = this.#displayY(snapped.y);
|
|
3066
3111
|
this.cursor.style.left = `${snapped.x * 100}%`;
|
|
3067
|
-
this.cursor.style.top = `${
|
|
3112
|
+
this.cursor.style.top = `${displayY * 100}%`;
|
|
3068
3113
|
if (this.text && this.xInput && this.yInput) {
|
|
3069
|
-
this.xInput.setAttribute("value", snapped.x
|
|
3070
|
-
this.yInput.setAttribute("value", snapped.y
|
|
3114
|
+
this.xInput.setAttribute("value", Math.round(snapped.x * 100));
|
|
3115
|
+
this.yInput.setAttribute("value", Math.round(snapped.y * 100));
|
|
3071
3116
|
}
|
|
3072
3117
|
|
|
3073
3118
|
this.#emitInputEvent();
|
|
@@ -3092,9 +3137,15 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3092
3137
|
}
|
|
3093
3138
|
|
|
3094
3139
|
#syncHandlePosition() {
|
|
3140
|
+
const displayY = this.#displayY(this.position.y);
|
|
3095
3141
|
if (this.cursor) {
|
|
3096
3142
|
this.cursor.style.left = `${this.position.x * 100}%`;
|
|
3097
|
-
this.cursor.style.top = `${
|
|
3143
|
+
this.cursor.style.top = `${displayY * 100}%`;
|
|
3144
|
+
}
|
|
3145
|
+
// Also sync text inputs if they exist (convert to percentage 0-100)
|
|
3146
|
+
if (this.text && this.xInput && this.yInput) {
|
|
3147
|
+
this.xInput.setAttribute("value", Math.round(this.position.x * 100));
|
|
3148
|
+
this.yInput.setAttribute("value", Math.round(this.position.y * 100));
|
|
3098
3149
|
}
|
|
3099
3150
|
}
|
|
3100
3151
|
|
|
@@ -3152,16 +3203,33 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3152
3203
|
#handleKeyUp(e) {
|
|
3153
3204
|
if (e.key === "Shift") this.isShiftHeld = false;
|
|
3154
3205
|
}
|
|
3206
|
+
focus() {
|
|
3207
|
+
const container = this.querySelector(".fig-input-joystick-plane-container");
|
|
3208
|
+
container?.focus();
|
|
3209
|
+
}
|
|
3155
3210
|
static get observedAttributes() {
|
|
3156
|
-
return ["value", "precision", "transform", "text"];
|
|
3211
|
+
return ["value", "precision", "transform", "text", "coordinates"];
|
|
3157
3212
|
}
|
|
3158
3213
|
get value() {
|
|
3159
|
-
|
|
3214
|
+
// Return as percentage values (0-100)
|
|
3215
|
+
return [
|
|
3216
|
+
Math.round(this.position.x * 100),
|
|
3217
|
+
Math.round(this.position.y * 100),
|
|
3218
|
+
];
|
|
3160
3219
|
}
|
|
3161
3220
|
set value(value) {
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3221
|
+
// Parse value, strip % symbols if present, convert from 0-100 to 0-1
|
|
3222
|
+
const v = value
|
|
3223
|
+
.toString()
|
|
3224
|
+
.split(",")
|
|
3225
|
+
.map((s) => {
|
|
3226
|
+
const num = parseFloat(s.replace(/%/g, "").trim());
|
|
3227
|
+
return isNaN(num) ? 0.5 : num / 100; // Convert from percentage to decimal, default to 0.5 if invalid
|
|
3228
|
+
});
|
|
3229
|
+
this.position = { x: v[0] ?? 0.5, y: v[1] ?? 0.5 };
|
|
3230
|
+
if (this.#initialized) {
|
|
3231
|
+
this.#syncHandlePosition();
|
|
3232
|
+
}
|
|
3165
3233
|
}
|
|
3166
3234
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
3167
3235
|
if (name === "value") {
|
|
@@ -3177,6 +3245,10 @@ class FigInputJoystick extends HTMLElement {
|
|
|
3177
3245
|
this.text = newValue.toLowerCase() === "true";
|
|
3178
3246
|
this.#render();
|
|
3179
3247
|
}
|
|
3248
|
+
if (name === "coordinates") {
|
|
3249
|
+
this.coordinates = newValue || "screen";
|
|
3250
|
+
this.#syncHandlePosition();
|
|
3251
|
+
}
|
|
3180
3252
|
}
|
|
3181
3253
|
}
|
|
3182
3254
|
|
|
@@ -3406,6 +3478,10 @@ class FigInputAngle extends HTMLElement {
|
|
|
3406
3478
|
if (e.key === "Shift") this.isShiftHeld = false;
|
|
3407
3479
|
}
|
|
3408
3480
|
|
|
3481
|
+
focus() {
|
|
3482
|
+
this.plane?.focus();
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3409
3485
|
static get observedAttributes() {
|
|
3410
3486
|
return ["value", "precision", "text"];
|
|
3411
3487
|
}
|
|
@@ -3446,3 +3522,37 @@ class FigInputAngle extends HTMLElement {
|
|
|
3446
3522
|
}
|
|
3447
3523
|
}
|
|
3448
3524
|
customElements.define("fig-input-angle", FigInputAngle);
|
|
3525
|
+
|
|
3526
|
+
// FigShimmer
|
|
3527
|
+
class FigShimmer extends HTMLElement {
|
|
3528
|
+
connectedCallback() {
|
|
3529
|
+
const duration = this.getAttribute("duration");
|
|
3530
|
+
if (duration) {
|
|
3531
|
+
this.style.setProperty("--shimmer-duration", duration);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
static get observedAttributes() {
|
|
3536
|
+
return ["duration", "playing"];
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3539
|
+
get playing() {
|
|
3540
|
+
return this.getAttribute("playing") !== "false";
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
set playing(value) {
|
|
3544
|
+
if (value) {
|
|
3545
|
+
this.removeAttribute("playing"); // Default is playing
|
|
3546
|
+
} else {
|
|
3547
|
+
this.setAttribute("playing", "false");
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
3552
|
+
if (name === "duration") {
|
|
3553
|
+
this.style.setProperty("--shimmer-duration", newValue || "1.5s");
|
|
3554
|
+
}
|
|
3555
|
+
// playing is handled purely by CSS attribute selectors
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
customElements.define("fig-shimmer", FigShimmer);
|