@rogieking/figui3 1.3.2 → 1.3.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/example.html +10 -1
- package/fig.css +70 -22
- package/fig.js +350 -26
- package/package.json +1 -1
package/example.html
CHANGED
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
<h2>UI3 Components</h2>
|
|
24
24
|
</fig-header>
|
|
25
25
|
<fig-content>
|
|
26
|
+
<fig-input-angle text="true"
|
|
27
|
+
value="0"
|
|
28
|
+
onInput="console.log(event.target.adjacent, event.target.opposite)"></fig-input-angle>
|
|
29
|
+
<br /><br />
|
|
30
|
+
<fig-input-joystick></fig-input-joystick>
|
|
31
|
+
<br /><br />
|
|
26
32
|
<fig-input-joystick text="true"
|
|
27
33
|
value="0,0"
|
|
28
34
|
onInput="console.log(event.target.value)"></fig-input-joystick>
|
|
@@ -445,8 +451,11 @@
|
|
|
445
451
|
</fig-header>
|
|
446
452
|
<fig-field>
|
|
447
453
|
<label>Switches</label>
|
|
448
|
-
<fig-switch
|
|
454
|
+
<fig-switch checked="true"
|
|
455
|
+
onInput="console.log(event.target.checked)"
|
|
449
456
|
label="On"></fig-switch>
|
|
457
|
+
<fig-switch checked="false"
|
|
458
|
+
label="Off"></fig-switch>
|
|
450
459
|
<fig-switch indeterminate="true"
|
|
451
460
|
label="Indeterminate"></fig-switch>
|
|
452
461
|
<fig-switch disabled
|
package/fig.css
CHANGED
|
@@ -415,7 +415,7 @@
|
|
|
415
415
|
--body-medium-fontWeight: 450;
|
|
416
416
|
--handle-shadow: 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
|
|
417
417
|
0px 1px 3px 0px rgba(0, 0, 0, 0.05), 0px 3px 8px 0px rgba(0, 0, 0, 0.05),
|
|
418
|
-
0px 0px 0.5px 0px rgba(0, 0, 0, 0.1);
|
|
418
|
+
0px 0px 0.5px 0px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
|
|
419
419
|
|
|
420
420
|
--popover-min-width: 15rem;
|
|
421
421
|
--figma-color-bg-ghost-hover: rgba(0, 0, 0, 0.05);
|
|
@@ -2263,22 +2263,11 @@ fig-segmented-control {
|
|
|
2263
2263
|
}
|
|
2264
2264
|
}
|
|
2265
2265
|
|
|
2266
|
-
/* Utilities */
|
|
2267
|
-
|
|
2268
|
-
@keyframes fig-spinner-spin {
|
|
2269
|
-
from {
|
|
2270
|
-
transform: rotate(0deg);
|
|
2271
|
-
}
|
|
2272
|
-
|
|
2273
|
-
to {
|
|
2274
|
-
transform: rotate(360deg);
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
|
|
2278
2266
|
fig-input-joystick {
|
|
2279
2267
|
--size: 1.5rem;
|
|
2280
2268
|
display: inline-flex;
|
|
2281
2269
|
gap: var(--spacer-2);
|
|
2270
|
+
user-select: none;
|
|
2282
2271
|
.fig-input-joystick-plane-container {
|
|
2283
2272
|
display: flex;
|
|
2284
2273
|
width: 1.5rem;
|
|
@@ -2288,6 +2277,9 @@ fig-input-joystick {
|
|
|
2288
2277
|
flex-grow: 0;
|
|
2289
2278
|
align-items: center;
|
|
2290
2279
|
justify-content: center;
|
|
2280
|
+
&:focus {
|
|
2281
|
+
outline: 0;
|
|
2282
|
+
}
|
|
2291
2283
|
}
|
|
2292
2284
|
.fig-input-joystick-plane {
|
|
2293
2285
|
display: inline-grid;
|
|
@@ -2296,10 +2288,10 @@ fig-input-joystick {
|
|
|
2296
2288
|
width: var(--size);
|
|
2297
2289
|
height: var(--size);
|
|
2298
2290
|
flex-shrink: 0;
|
|
2299
|
-
&.dragging
|
|
2300
|
-
&:hover {
|
|
2291
|
+
&.dragging {
|
|
2301
2292
|
cursor: grab;
|
|
2302
2293
|
--size: 3rem;
|
|
2294
|
+
z-index: 2;
|
|
2303
2295
|
}
|
|
2304
2296
|
}
|
|
2305
2297
|
.fig-input-joystick-plane > * {
|
|
@@ -2309,7 +2301,7 @@ fig-input-joystick {
|
|
|
2309
2301
|
position: absolute;
|
|
2310
2302
|
width: var(--size);
|
|
2311
2303
|
height: var(--size);
|
|
2312
|
-
|
|
2304
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-border);
|
|
2313
2305
|
border-radius: var(--radius-medium);
|
|
2314
2306
|
overflow: hidden;
|
|
2315
2307
|
background: var(--figma-color-bg-secondary);
|
|
@@ -2319,8 +2311,8 @@ fig-input-joystick {
|
|
|
2319
2311
|
position: absolute;
|
|
2320
2312
|
height: 0;
|
|
2321
2313
|
border-left: 1px solid var(--figma-color-border);
|
|
2322
|
-
height: 100
|
|
2323
|
-
top:
|
|
2314
|
+
height: calc(100% - 2px);
|
|
2315
|
+
top: 1px;
|
|
2324
2316
|
left: calc(50% - 0.5px);
|
|
2325
2317
|
pointer-events: none;
|
|
2326
2318
|
}
|
|
@@ -2329,14 +2321,13 @@ fig-input-joystick {
|
|
|
2329
2321
|
position: absolute;
|
|
2330
2322
|
height: 0;
|
|
2331
2323
|
border-top: 1px solid var(--figma-color-border);
|
|
2332
|
-
width: 100
|
|
2324
|
+
width: calc(100% - 2px);
|
|
2333
2325
|
top: calc(50% - 0.5px);
|
|
2334
|
-
left:
|
|
2326
|
+
left: 1px;
|
|
2335
2327
|
pointer-events: none;
|
|
2336
2328
|
}
|
|
2337
2329
|
}
|
|
2338
|
-
.fig-input-joystick-plane.dragging .fig-input-joystick-guides
|
|
2339
|
-
.fig-input-joystick-plane:hover .fig-input-joystick-guides {
|
|
2330
|
+
.fig-input-joystick-plane.dragging .fig-input-joystick-guides {
|
|
2340
2331
|
background: linear-gradient(
|
|
2341
2332
|
45deg,
|
|
2342
2333
|
transparent calc(50% - 0.5px),
|
|
@@ -2352,7 +2343,12 @@ fig-input-joystick {
|
|
|
2352
2343
|
transparent calc(50% + 0.5px)
|
|
2353
2344
|
),
|
|
2354
2345
|
var(--figma-color-bg-secondary);
|
|
2346
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
|
|
2347
|
+
}
|
|
2348
|
+
.fig-input-joystick-plane-container:focus .fig-input-joystick-guides {
|
|
2349
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
|
|
2355
2350
|
}
|
|
2351
|
+
|
|
2356
2352
|
.fig-input-joystick-handle {
|
|
2357
2353
|
position: absolute;
|
|
2358
2354
|
z-index: 1;
|
|
@@ -2364,3 +2360,55 @@ fig-input-joystick {
|
|
|
2364
2360
|
transform: translate(-50%, -50%);
|
|
2365
2361
|
}
|
|
2366
2362
|
}
|
|
2363
|
+
|
|
2364
|
+
fig-input-angle {
|
|
2365
|
+
--size: 1.5rem;
|
|
2366
|
+
display: inline-flex;
|
|
2367
|
+
gap: var(--spacer-2);
|
|
2368
|
+
user-select: none;
|
|
2369
|
+
|
|
2370
|
+
.fig-input-angle-plane {
|
|
2371
|
+
display: inline-grid;
|
|
2372
|
+
place-items: center;
|
|
2373
|
+
width: var(--size);
|
|
2374
|
+
height: var(--size);
|
|
2375
|
+
aspect-ratio: 1/1;
|
|
2376
|
+
flex-shrink: 0;
|
|
2377
|
+
background-color: var(--figma-color-bg-secondary);
|
|
2378
|
+
border-radius: 100%;
|
|
2379
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-border);
|
|
2380
|
+
&:focus,
|
|
2381
|
+
&.dragging {
|
|
2382
|
+
outline: 0;
|
|
2383
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-border-selected);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
.fig-input-angle-handle {
|
|
2387
|
+
display: inline-grid;
|
|
2388
|
+
place-items: center;
|
|
2389
|
+
grid-area: 1/1;
|
|
2390
|
+
width: calc(0.5rem + 2px);
|
|
2391
|
+
height: calc(0.5rem + 2px);
|
|
2392
|
+
&:before {
|
|
2393
|
+
content: "";
|
|
2394
|
+
display: block;
|
|
2395
|
+
width: 0.5rem;
|
|
2396
|
+
height: 0.5rem;
|
|
2397
|
+
background: var(--figma-color-icon-onbrand);
|
|
2398
|
+
box-shadow: var(--handle-shadow);
|
|
2399
|
+
border-radius: 50%;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
/* Utilities */
|
|
2405
|
+
|
|
2406
|
+
@keyframes fig-spinner-spin {
|
|
2407
|
+
from {
|
|
2408
|
+
transform: rotate(0deg);
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
to {
|
|
2412
|
+
transform: rotate(360deg);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
package/fig.js
CHANGED
|
@@ -1671,6 +1671,7 @@ class FigImage extends HTMLElement {
|
|
|
1671
1671
|
this.innerHTML = this.#getInnerHTML();
|
|
1672
1672
|
this.#updateRefs();
|
|
1673
1673
|
}
|
|
1674
|
+
|
|
1674
1675
|
#updateRefs() {
|
|
1675
1676
|
requestAnimationFrame(() => {
|
|
1676
1677
|
this.chit = this.querySelector("fig-chit");
|
|
@@ -1712,6 +1713,13 @@ class FigImage extends HTMLElement {
|
|
|
1712
1713
|
}
|
|
1713
1714
|
window.customElements.define("fig-image", FigImage);
|
|
1714
1715
|
|
|
1716
|
+
/**
|
|
1717
|
+
* A custom joystick input element.
|
|
1718
|
+
* @attr {string} value - The current position of the joystick (e.g., "0.5,0.5").
|
|
1719
|
+
* @attr {number} precision - The number of decimal places for the output.
|
|
1720
|
+
* @attr {number} transform - A scaling factor for the output.
|
|
1721
|
+
* @attr {boolean} text - Whether to display text inputs for X and Y values.
|
|
1722
|
+
*/
|
|
1715
1723
|
class FigInputJoystick extends HTMLElement {
|
|
1716
1724
|
constructor() {
|
|
1717
1725
|
super();
|
|
@@ -1720,6 +1728,10 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1720
1728
|
this.value = [0.5, 0.5];
|
|
1721
1729
|
this.isDragging = false;
|
|
1722
1730
|
this.isShiftHeld = false;
|
|
1731
|
+
this.plane = null;
|
|
1732
|
+
this.cursor = null;
|
|
1733
|
+
this.xInput = null;
|
|
1734
|
+
this.yInput = null;
|
|
1723
1735
|
|
|
1724
1736
|
// Initialize position
|
|
1725
1737
|
requestAnimationFrame(() => {
|
|
@@ -1734,19 +1746,28 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1734
1746
|
this.#setupListeners();
|
|
1735
1747
|
|
|
1736
1748
|
this.#syncHandlePosition();
|
|
1737
|
-
if (this.text) {
|
|
1738
|
-
this.xInput.
|
|
1739
|
-
|
|
1749
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1750
|
+
this.xInput.setAttribute(
|
|
1751
|
+
"value",
|
|
1752
|
+
this.position.x.toFixed(this.precision)
|
|
1753
|
+
);
|
|
1754
|
+
this.yInput.setAttribute(
|
|
1755
|
+
"value",
|
|
1756
|
+
this.position.y.toFixed(this.precision)
|
|
1757
|
+
);
|
|
1740
1758
|
}
|
|
1741
1759
|
});
|
|
1742
1760
|
}
|
|
1761
|
+
disconnectedCallback() {
|
|
1762
|
+
this.#cleanupListeners();
|
|
1763
|
+
}
|
|
1743
1764
|
|
|
1744
1765
|
#render() {
|
|
1745
1766
|
this.innerHTML = this.#getInnerHTML();
|
|
1746
1767
|
}
|
|
1747
1768
|
#getInnerHTML() {
|
|
1748
1769
|
return `
|
|
1749
|
-
<div class="fig-input-joystick-plane-container">
|
|
1770
|
+
<div class="fig-input-joystick-plane-container" tabindex="0">
|
|
1750
1771
|
<div class="fig-input-joystick-plane">
|
|
1751
1772
|
<div class="fig-input-joystick-guides"></div>
|
|
1752
1773
|
<div class="fig-input-joystick-handle"></div>
|
|
@@ -1780,23 +1801,34 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1780
1801
|
#setupListeners() {
|
|
1781
1802
|
this.plane = this.querySelector(".fig-input-joystick-plane");
|
|
1782
1803
|
this.cursor = this.querySelector(".fig-input-joystick-handle");
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
this.yInput = this.querySelector("fig-input-text[name='y']");
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1804
|
+
this.xInput = this.querySelector("fig-input-text[name='x']");
|
|
1805
|
+
this.yInput = this.querySelector("fig-input-text[name='y']");
|
|
1788
1806
|
this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
|
|
1807
|
+
this.plane.addEventListener(
|
|
1808
|
+
"touchstart",
|
|
1809
|
+
this.#handleTouchStart.bind(this)
|
|
1810
|
+
);
|
|
1789
1811
|
window.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
1790
1812
|
window.addEventListener("keyup", this.#handleKeyUp.bind(this));
|
|
1813
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1814
|
+
this.xInput.addEventListener("input", this.#handleXInput.bind(this));
|
|
1815
|
+
this.yInput.addEventListener("input", this.#handleYInput.bind(this));
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1791
1818
|
|
|
1792
|
-
|
|
1793
|
-
this.
|
|
1819
|
+
#cleanupListeners() {
|
|
1820
|
+
this.plane.removeEventListener("mousedown", this.#handleMouseDown);
|
|
1821
|
+
window.removeEventListener("keydown", this.#handleKeyDown);
|
|
1822
|
+
window.removeEventListener("keyup", this.#handleKeyUp);
|
|
1823
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1824
|
+
this.xInput.removeEventListener("input", this.#handleXInput);
|
|
1825
|
+
this.yInput.removeEventListener("input", this.#handleYInput);
|
|
1826
|
+
}
|
|
1794
1827
|
}
|
|
1795
1828
|
|
|
1796
1829
|
#handleXInput(e) {
|
|
1797
1830
|
e.stopPropagation();
|
|
1798
1831
|
this.position.x = Number(e.target.value);
|
|
1799
|
-
this.value = [this.position.x, this.position.y];
|
|
1800
1832
|
this.#syncHandlePosition();
|
|
1801
1833
|
this.#emitInputEvent();
|
|
1802
1834
|
this.#emitChangeEvent();
|
|
@@ -1805,7 +1837,6 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1805
1837
|
#handleYInput(e) {
|
|
1806
1838
|
e.stopPropagation();
|
|
1807
1839
|
this.position.y = Number(e.target.value);
|
|
1808
|
-
this.value = [this.position.x, this.position.y];
|
|
1809
1840
|
this.#syncHandlePosition();
|
|
1810
1841
|
this.#emitInputEvent();
|
|
1811
1842
|
this.#emitChangeEvent();
|
|
@@ -1837,17 +1868,17 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1837
1868
|
|
|
1838
1869
|
const snapped = this.#snapToDiagonal(x, y);
|
|
1839
1870
|
this.position = snapped;
|
|
1840
|
-
this.value = [snapped.x, snapped.y];
|
|
1841
1871
|
|
|
1842
1872
|
this.cursor.style.left = `${snapped.x * 100}%`;
|
|
1843
|
-
this.cursor.style.top = `${
|
|
1844
|
-
if (this.text) {
|
|
1873
|
+
this.cursor.style.top = `${snapped.y * 100}%`; // Invert Y for display
|
|
1874
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1845
1875
|
this.xInput.setAttribute("value", snapped.x.toFixed(3));
|
|
1846
1876
|
this.yInput.setAttribute("value", snapped.y.toFixed(3));
|
|
1847
1877
|
}
|
|
1848
1878
|
|
|
1849
1879
|
this.#emitInputEvent();
|
|
1850
1880
|
}
|
|
1881
|
+
|
|
1851
1882
|
#emitInputEvent() {
|
|
1852
1883
|
this.dispatchEvent(
|
|
1853
1884
|
new CustomEvent("input", {
|
|
@@ -1875,12 +1906,13 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1875
1906
|
|
|
1876
1907
|
#handleMouseDown(e) {
|
|
1877
1908
|
this.isDragging = true;
|
|
1878
|
-
|
|
1909
|
+
|
|
1879
1910
|
this.#updatePosition(e);
|
|
1880
1911
|
|
|
1881
1912
|
this.plane.style.cursor = "grabbing";
|
|
1882
1913
|
|
|
1883
1914
|
const handleMouseMove = (e) => {
|
|
1915
|
+
this.plane.classList.add("dragging");
|
|
1884
1916
|
if (this.isDragging) this.#updatePosition(e);
|
|
1885
1917
|
};
|
|
1886
1918
|
|
|
@@ -1897,6 +1929,27 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1897
1929
|
window.addEventListener("mouseup", handleMouseUp);
|
|
1898
1930
|
}
|
|
1899
1931
|
|
|
1932
|
+
#handleTouchStart(e) {
|
|
1933
|
+
e.preventDefault();
|
|
1934
|
+
this.isDragging = true;
|
|
1935
|
+
this.#updatePosition(e.touches[0]);
|
|
1936
|
+
|
|
1937
|
+
const handleTouchMove = (e) => {
|
|
1938
|
+
if (this.isDragging) this.#updatePosition(e.touches[0]);
|
|
1939
|
+
};
|
|
1940
|
+
|
|
1941
|
+
const handleTouchEnd = () => {
|
|
1942
|
+
this.isDragging = false;
|
|
1943
|
+
this.plane.classList.remove("dragging");
|
|
1944
|
+
window.removeEventListener("touchmove", handleTouchMove);
|
|
1945
|
+
window.removeEventListener("touchend", handleTouchEnd);
|
|
1946
|
+
this.#emitChangeEvent();
|
|
1947
|
+
};
|
|
1948
|
+
|
|
1949
|
+
window.addEventListener("touchmove", handleTouchMove);
|
|
1950
|
+
window.addEventListener("touchend", handleTouchEnd);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1900
1953
|
#handleKeyDown(e) {
|
|
1901
1954
|
if (e.key === "Shift") this.isShiftHeld = true;
|
|
1902
1955
|
}
|
|
@@ -1907,25 +1960,296 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1907
1960
|
static get observedAttributes() {
|
|
1908
1961
|
return ["value", "precision", "transform", "text"];
|
|
1909
1962
|
}
|
|
1963
|
+
get value() {
|
|
1964
|
+
return [this.position.x, this.position.y];
|
|
1965
|
+
}
|
|
1966
|
+
set value(value) {
|
|
1967
|
+
let v = value.toString().split(",").map(Number);
|
|
1968
|
+
this.position = { x: v[0], y: v[1] };
|
|
1969
|
+
this.#syncHandlePosition();
|
|
1970
|
+
}
|
|
1910
1971
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
1911
1972
|
if (name === "value") {
|
|
1912
|
-
this.value = newValue
|
|
1913
|
-
this.position = { x: this.value[0], y: this.value[1] };
|
|
1914
|
-
this.#syncHandlePosition();
|
|
1973
|
+
this.value = newValue;
|
|
1915
1974
|
}
|
|
1916
1975
|
if (name === "precision") {
|
|
1917
|
-
this.precision = newValue;
|
|
1918
|
-
this.precision = parseInt(this.precision);
|
|
1976
|
+
this.precision = parseInt(newValue);
|
|
1919
1977
|
}
|
|
1920
1978
|
if (name === "transform") {
|
|
1921
|
-
this.transform = newValue;
|
|
1922
|
-
this.transform = Number(this.transform);
|
|
1979
|
+
this.transform = Number(newValue);
|
|
1923
1980
|
}
|
|
1924
|
-
if (name === "text") {
|
|
1981
|
+
if (name === "text" && newValue !== oldValue) {
|
|
1925
1982
|
this.text = newValue.toLowerCase() === "true";
|
|
1926
|
-
this
|
|
1983
|
+
this.#render();
|
|
1927
1984
|
}
|
|
1928
1985
|
}
|
|
1929
1986
|
}
|
|
1930
1987
|
|
|
1931
1988
|
customElements.define("fig-input-joystick", FigInputJoystick);
|
|
1989
|
+
|
|
1990
|
+
/**
|
|
1991
|
+
* A custom angle chooser input element.
|
|
1992
|
+
* @attr {number} value - The current angle of the handle in degrees.
|
|
1993
|
+
* @attr {number} precision - The number of decimal places for the output.
|
|
1994
|
+
* @attr {boolean} text - Whether to display a text input for the angle value.
|
|
1995
|
+
* @attr {number} adjacent - The adjacent value of the angle.
|
|
1996
|
+
* @attr {number} opposite - The opposite value of the angle.
|
|
1997
|
+
*/
|
|
1998
|
+
class FigInputAngle extends HTMLElement {
|
|
1999
|
+
// Declare private fields first
|
|
2000
|
+
#adjacent;
|
|
2001
|
+
#opposite;
|
|
2002
|
+
|
|
2003
|
+
constructor() {
|
|
2004
|
+
super();
|
|
2005
|
+
|
|
2006
|
+
this.angle = 0; // Angle in degrees
|
|
2007
|
+
this.#adjacent = 1;
|
|
2008
|
+
this.#opposite = 0;
|
|
2009
|
+
this.isDragging = false;
|
|
2010
|
+
this.isShiftHeld = false;
|
|
2011
|
+
this.handle = null;
|
|
2012
|
+
this.angleInput = null;
|
|
2013
|
+
this.plane = null;
|
|
2014
|
+
|
|
2015
|
+
// Initialize position
|
|
2016
|
+
requestAnimationFrame(() => {
|
|
2017
|
+
this.precision = this.getAttribute("precision") || 1;
|
|
2018
|
+
this.precision = parseInt(this.precision);
|
|
2019
|
+
this.text = this.getAttribute("text") === "true";
|
|
2020
|
+
|
|
2021
|
+
this.#render();
|
|
2022
|
+
|
|
2023
|
+
this.#setupListeners();
|
|
2024
|
+
|
|
2025
|
+
this.#syncHandlePosition();
|
|
2026
|
+
if (this.text && this.angleInput) {
|
|
2027
|
+
this.angleInput.setAttribute(
|
|
2028
|
+
"value",
|
|
2029
|
+
this.angle.toFixed(this.precision)
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
disconnectedCallback() {
|
|
2036
|
+
this.#cleanupListeners();
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
#render() {
|
|
2040
|
+
this.innerHTML = this.#getInnerHTML();
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
#getInnerHTML() {
|
|
2044
|
+
return `
|
|
2045
|
+
<div class="fig-input-angle-plane" tabindex="0">
|
|
2046
|
+
<div class="fig-input-angle-handle"></div>
|
|
2047
|
+
</div>
|
|
2048
|
+
${
|
|
2049
|
+
this.text
|
|
2050
|
+
? `<fig-input-text
|
|
2051
|
+
type="number"
|
|
2052
|
+
name="angle"
|
|
2053
|
+
step="0.1"
|
|
2054
|
+
value="${this.angle}"
|
|
2055
|
+
min="0"
|
|
2056
|
+
max="360">
|
|
2057
|
+
<span slot="append">°</span>
|
|
2058
|
+
</fig-input-text>`
|
|
2059
|
+
: ""
|
|
2060
|
+
}
|
|
2061
|
+
`;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
#setupListeners() {
|
|
2065
|
+
this.handle = this.querySelector(".fig-input-angle-handle");
|
|
2066
|
+
this.plane = this.querySelector(".fig-input-angle-plane");
|
|
2067
|
+
this.angleInput = this.querySelector("fig-input-text[name='angle']");
|
|
2068
|
+
this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
|
|
2069
|
+
this.plane.addEventListener(
|
|
2070
|
+
"touchstart",
|
|
2071
|
+
this.#handleTouchStart.bind(this)
|
|
2072
|
+
);
|
|
2073
|
+
window.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
2074
|
+
window.addEventListener("keyup", this.#handleKeyUp.bind(this));
|
|
2075
|
+
if (this.text && this.angleInput) {
|
|
2076
|
+
this.angleInput = this.querySelector("fig-input-text");
|
|
2077
|
+
this.angleInput.addEventListener(
|
|
2078
|
+
"input",
|
|
2079
|
+
this.#handleAngleInput.bind(this)
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
#cleanupListeners() {
|
|
2085
|
+
this.plane.removeEventListener("mousedown", this.#handleMouseDown);
|
|
2086
|
+
this.plane.removeEventListener("touchstart", this.#handleTouchStart);
|
|
2087
|
+
window.removeEventListener("keydown", this.#handleKeyDown);
|
|
2088
|
+
window.removeEventListener("keyup", this.#handleKeyUp);
|
|
2089
|
+
if (this.text && this.angleInput) {
|
|
2090
|
+
this.angleInput.removeEventListener("input", this.#handleAngleInput);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
#handleAngleInput(e) {
|
|
2095
|
+
e.stopPropagation();
|
|
2096
|
+
this.angle = Number(e.target.value);
|
|
2097
|
+
this.#calculateAdjacentAndOpposite();
|
|
2098
|
+
this.#syncHandlePosition();
|
|
2099
|
+
this.#emitInputEvent();
|
|
2100
|
+
this.#emitChangeEvent();
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
#calculateAdjacentAndOpposite() {
|
|
2104
|
+
const radians = (this.angle * Math.PI) / 180;
|
|
2105
|
+
this.#adjacent = Math.cos(radians);
|
|
2106
|
+
this.#opposite = Math.sin(radians);
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
#snapToIncrement(angle) {
|
|
2110
|
+
if (!this.isShiftHeld) return angle;
|
|
2111
|
+
const increment = 45;
|
|
2112
|
+
return Math.round(angle / increment) * increment;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
#updateAngle(e) {
|
|
2116
|
+
const rect = this.plane.getBoundingClientRect();
|
|
2117
|
+
const centerX = rect.left + rect.width / 2;
|
|
2118
|
+
const centerY = rect.top + rect.height / 2;
|
|
2119
|
+
const deltaX = e.clientX - centerX;
|
|
2120
|
+
const deltaY = e.clientY - centerY;
|
|
2121
|
+
let angle = ((Math.atan2(deltaY, deltaX) * 180) / Math.PI + 360) % 360;
|
|
2122
|
+
|
|
2123
|
+
angle = this.#snapToIncrement(angle);
|
|
2124
|
+
this.angle = angle;
|
|
2125
|
+
|
|
2126
|
+
this.#calculateAdjacentAndOpposite();
|
|
2127
|
+
|
|
2128
|
+
this.#syncHandlePosition();
|
|
2129
|
+
if (this.text && this.angleInput) {
|
|
2130
|
+
this.angleInput.setAttribute("value", this.angle.toFixed(this.precision));
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
this.#emitInputEvent();
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
#emitInputEvent() {
|
|
2137
|
+
this.dispatchEvent(
|
|
2138
|
+
new CustomEvent("input", {
|
|
2139
|
+
bubbles: true,
|
|
2140
|
+
cancelable: true,
|
|
2141
|
+
})
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
#emitChangeEvent() {
|
|
2146
|
+
this.dispatchEvent(
|
|
2147
|
+
new CustomEvent("change", {
|
|
2148
|
+
bubbles: true,
|
|
2149
|
+
cancelable: true,
|
|
2150
|
+
})
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
#syncHandlePosition() {
|
|
2155
|
+
if (this.handle) {
|
|
2156
|
+
const radians = (this.angle * Math.PI) / 180;
|
|
2157
|
+
const radius = this.plane.offsetWidth / 2 - this.handle.offsetWidth / 2;
|
|
2158
|
+
const x = Math.cos(radians) * radius;
|
|
2159
|
+
const y = Math.sin(radians) * radius;
|
|
2160
|
+
this.handle.style.transform = `translate(${x}px, ${y}px)`;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
#handleMouseDown(e) {
|
|
2165
|
+
this.isDragging = true;
|
|
2166
|
+
this.#updateAngle(e);
|
|
2167
|
+
|
|
2168
|
+
const handleMouseMove = (e) => {
|
|
2169
|
+
this.plane.classList.add("dragging");
|
|
2170
|
+
if (this.isDragging) this.#updateAngle(e);
|
|
2171
|
+
};
|
|
2172
|
+
|
|
2173
|
+
const handleMouseUp = () => {
|
|
2174
|
+
this.isDragging = false;
|
|
2175
|
+
this.plane.classList.remove("dragging");
|
|
2176
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
2177
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
2178
|
+
this.#emitChangeEvent();
|
|
2179
|
+
};
|
|
2180
|
+
|
|
2181
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
2182
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
#handleTouchStart(e) {
|
|
2186
|
+
e.preventDefault();
|
|
2187
|
+
this.isDragging = true;
|
|
2188
|
+
this.#updateAngle(e.touches[0]);
|
|
2189
|
+
|
|
2190
|
+
const handleTouchMove = (e) => {
|
|
2191
|
+
this.plane.classList.add("dragging");
|
|
2192
|
+
if (this.isDragging) this.#updateAngle(e.touches[0]);
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2195
|
+
const handleTouchEnd = () => {
|
|
2196
|
+
this.isDragging = false;
|
|
2197
|
+
this.plane.classList.remove("dragging");
|
|
2198
|
+
window.removeEventListener("touchmove", handleTouchMove);
|
|
2199
|
+
window.removeEventListener("touchend", handleTouchEnd);
|
|
2200
|
+
this.#emitChangeEvent();
|
|
2201
|
+
};
|
|
2202
|
+
|
|
2203
|
+
window.addEventListener("touchmove", handleTouchMove);
|
|
2204
|
+
window.addEventListener("touchend", handleTouchEnd);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
#handleKeyDown(e) {
|
|
2208
|
+
if (e.key === "Shift") this.isShiftHeld = true;
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
#handleKeyUp(e) {
|
|
2212
|
+
if (e.key === "Shift") this.isShiftHeld = false;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
static get observedAttributes() {
|
|
2216
|
+
return ["value", "precision", "text"];
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
get value() {
|
|
2220
|
+
return this.angle;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
get adjacent() {
|
|
2224
|
+
return this.#adjacent;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
get opposite() {
|
|
2228
|
+
return this.#opposite;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
set value(value) {
|
|
2232
|
+
if (isNaN(value)) {
|
|
2233
|
+
console.error("Invalid value: must be a number.");
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
this.angle = value;
|
|
2237
|
+
this.#calculateAdjacentAndOpposite();
|
|
2238
|
+
this.#syncHandlePosition();
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
2242
|
+
if (name === "value") {
|
|
2243
|
+
this.value = Number(newValue);
|
|
2244
|
+
}
|
|
2245
|
+
if (name === "precision") {
|
|
2246
|
+
this.precision = parseInt(newValue);
|
|
2247
|
+
}
|
|
2248
|
+
if (name === "text" && newValue !== oldValue) {
|
|
2249
|
+
this.text = newValue.toLowerCase() === "true";
|
|
2250
|
+
this.#render();
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
customElements.define("fig-input-angle", FigInputAngle);
|