@rogieking/figui3 1.3.1 → 1.3.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/example.html +11 -1
- package/fig.css +58 -14
- package/fig.js +355 -39
- package/package.json +1 -1
package/example.html
CHANGED
|
@@ -23,7 +23,14 @@
|
|
|
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.value)"></fig-input-angle>
|
|
29
|
+
<br /><br />
|
|
30
|
+
<fig-input-joystick></fig-input-joystick>
|
|
31
|
+
<br /><br />
|
|
26
32
|
<fig-input-joystick text="true"
|
|
33
|
+
value="0,0"
|
|
27
34
|
onInput="console.log(event.target.value)"></fig-input-joystick>
|
|
28
35
|
<fig-header>
|
|
29
36
|
<h2>Details</h2>
|
|
@@ -444,8 +451,11 @@
|
|
|
444
451
|
</fig-header>
|
|
445
452
|
<fig-field>
|
|
446
453
|
<label>Switches</label>
|
|
447
|
-
<fig-switch
|
|
454
|
+
<fig-switch checked="true"
|
|
455
|
+
onInput="console.log(event.target.checked)"
|
|
448
456
|
label="On"></fig-switch>
|
|
457
|
+
<fig-switch checked="false"
|
|
458
|
+
label="Off"></fig-switch>
|
|
449
459
|
<fig-switch indeterminate="true"
|
|
450
460
|
label="Indeterminate"></fig-switch>
|
|
451
461
|
<fig-switch disabled
|
package/fig.css
CHANGED
|
@@ -2263,18 +2263,6 @@ 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;
|
|
@@ -2284,6 +2272,10 @@ fig-input-joystick {
|
|
|
2284
2272
|
width: 1.5rem;
|
|
2285
2273
|
height: 1.5rem;
|
|
2286
2274
|
place-items: center;
|
|
2275
|
+
flex-shrink: 0;
|
|
2276
|
+
flex-grow: 0;
|
|
2277
|
+
align-items: center;
|
|
2278
|
+
justify-content: center;
|
|
2287
2279
|
}
|
|
2288
2280
|
.fig-input-joystick-plane {
|
|
2289
2281
|
display: inline-grid;
|
|
@@ -2292,9 +2284,10 @@ fig-input-joystick {
|
|
|
2292
2284
|
width: var(--size);
|
|
2293
2285
|
height: var(--size);
|
|
2294
2286
|
flex-shrink: 0;
|
|
2295
|
-
|
|
2287
|
+
&.dragging {
|
|
2296
2288
|
cursor: grab;
|
|
2297
2289
|
--size: 3rem;
|
|
2290
|
+
z-index: 2;
|
|
2298
2291
|
}
|
|
2299
2292
|
}
|
|
2300
2293
|
.fig-input-joystick-plane > * {
|
|
@@ -2330,7 +2323,7 @@ fig-input-joystick {
|
|
|
2330
2323
|
pointer-events: none;
|
|
2331
2324
|
}
|
|
2332
2325
|
}
|
|
2333
|
-
.fig-input-joystick-plane
|
|
2326
|
+
.fig-input-joystick-plane.dragging .fig-input-joystick-guides {
|
|
2334
2327
|
background: linear-gradient(
|
|
2335
2328
|
45deg,
|
|
2336
2329
|
transparent calc(50% - 0.5px),
|
|
@@ -2358,3 +2351,54 @@ fig-input-joystick {
|
|
|
2358
2351
|
transform: translate(-50%, -50%);
|
|
2359
2352
|
}
|
|
2360
2353
|
}
|
|
2354
|
+
|
|
2355
|
+
fig-input-angle {
|
|
2356
|
+
--size: 1.5rem;
|
|
2357
|
+
display: inline-flex;
|
|
2358
|
+
gap: var(--spacer-2);
|
|
2359
|
+
|
|
2360
|
+
.fig-input-angle-plane {
|
|
2361
|
+
display: inline-grid;
|
|
2362
|
+
place-items: center;
|
|
2363
|
+
width: var(--size);
|
|
2364
|
+
height: var(--size);
|
|
2365
|
+
aspect-ratio: 1/1;
|
|
2366
|
+
flex-shrink: 0;
|
|
2367
|
+
background-color: var(--figma-color-bg-secondary);
|
|
2368
|
+
border-radius: 100%;
|
|
2369
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-border);
|
|
2370
|
+
&.dragging {
|
|
2371
|
+
cursor: grab;
|
|
2372
|
+
--size: 3rem;
|
|
2373
|
+
z-index: 2;
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
.fig-input-angle-handle {
|
|
2377
|
+
display: inline-grid;
|
|
2378
|
+
place-items: center;
|
|
2379
|
+
grid-area: 1/1;
|
|
2380
|
+
width: calc(0.5rem + 2px);
|
|
2381
|
+
height: calc(0.5rem + 2px);
|
|
2382
|
+
&:before {
|
|
2383
|
+
content: "";
|
|
2384
|
+
display: block;
|
|
2385
|
+
width: 0.5rem;
|
|
2386
|
+
height: 0.5rem;
|
|
2387
|
+
background: var(--figma-color-icon-onbrand);
|
|
2388
|
+
box-shadow: var(--handle-shadow);
|
|
2389
|
+
border-radius: 50%;
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
/* Utilities */
|
|
2395
|
+
|
|
2396
|
+
@keyframes fig-spinner-spin {
|
|
2397
|
+
from {
|
|
2398
|
+
transform: rotate(0deg);
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
to {
|
|
2402
|
+
transform: rotate(360deg);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
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(() => {
|
|
@@ -1729,21 +1741,32 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1729
1741
|
this.transform = Number(this.transform);
|
|
1730
1742
|
this.text = this.getAttribute("text") === "true";
|
|
1731
1743
|
|
|
1732
|
-
this
|
|
1744
|
+
this.#render();
|
|
1733
1745
|
|
|
1734
|
-
this
|
|
1746
|
+
this.#setupListeners();
|
|
1735
1747
|
|
|
1736
|
-
this
|
|
1737
|
-
this.
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1748
|
+
this.#syncHandlePosition();
|
|
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
|
+
);
|
|
1741
1758
|
}
|
|
1742
1759
|
});
|
|
1743
1760
|
}
|
|
1761
|
+
disconnectedCallback() {
|
|
1762
|
+
this.#cleanupListeners();
|
|
1763
|
+
}
|
|
1744
1764
|
|
|
1745
|
-
render() {
|
|
1746
|
-
this.innerHTML =
|
|
1765
|
+
#render() {
|
|
1766
|
+
this.innerHTML = this.#getInnerHTML();
|
|
1767
|
+
}
|
|
1768
|
+
#getInnerHTML() {
|
|
1769
|
+
return `
|
|
1747
1770
|
<div class="fig-input-joystick-plane-container">
|
|
1748
1771
|
<div class="fig-input-joystick-plane">
|
|
1749
1772
|
<div class="fig-input-joystick-guides"></div>
|
|
@@ -1775,41 +1798,49 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1775
1798
|
`;
|
|
1776
1799
|
}
|
|
1777
1800
|
|
|
1778
|
-
setupListeners() {
|
|
1801
|
+
#setupListeners() {
|
|
1779
1802
|
this.plane = this.querySelector(".fig-input-joystick-plane");
|
|
1780
1803
|
this.cursor = this.querySelector(".fig-input-joystick-handle");
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1804
|
+
this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
|
|
1805
|
+
this.plane.addEventListener(
|
|
1806
|
+
"touchstart",
|
|
1807
|
+
this.#handleTouchStart.bind(this)
|
|
1808
|
+
);
|
|
1809
|
+
window.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
1810
|
+
window.addEventListener("keyup", this.#handleKeyUp.bind(this));
|
|
1811
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1812
|
+
this.xInput.addEventListener("input", this.#handleXInput.bind(this));
|
|
1813
|
+
this.yInput.addEventListener("input", this.#handleYInput.bind(this));
|
|
1784
1814
|
}
|
|
1815
|
+
}
|
|
1785
1816
|
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
window.
|
|
1789
|
-
|
|
1790
|
-
this.xInput
|
|
1791
|
-
|
|
1817
|
+
#cleanupListeners() {
|
|
1818
|
+
this.plane.removeEventListener("mousedown", this.#handleMouseDown);
|
|
1819
|
+
window.removeEventListener("keydown", this.#handleKeyDown);
|
|
1820
|
+
window.removeEventListener("keyup", this.#handleKeyUp);
|
|
1821
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1822
|
+
this.xInput.removeEventListener("input", this.#handleXInput);
|
|
1823
|
+
this.yInput.removeEventListener("input", this.#handleYInput);
|
|
1824
|
+
}
|
|
1792
1825
|
}
|
|
1793
1826
|
|
|
1794
|
-
handleXInput(e) {
|
|
1827
|
+
#handleXInput(e) {
|
|
1795
1828
|
e.stopPropagation();
|
|
1796
1829
|
this.position.x = Number(e.target.value);
|
|
1797
|
-
this.value = [this.position.x, this.position.y];
|
|
1798
1830
|
this.#syncHandlePosition();
|
|
1799
1831
|
this.#emitInputEvent();
|
|
1800
1832
|
this.#emitChangeEvent();
|
|
1801
1833
|
}
|
|
1802
1834
|
|
|
1803
|
-
handleYInput(e) {
|
|
1835
|
+
#handleYInput(e) {
|
|
1804
1836
|
e.stopPropagation();
|
|
1805
1837
|
this.position.y = Number(e.target.value);
|
|
1806
|
-
this.value = [this.position.x, this.position.y];
|
|
1807
1838
|
this.#syncHandlePosition();
|
|
1808
1839
|
this.#emitInputEvent();
|
|
1809
1840
|
this.#emitChangeEvent();
|
|
1810
1841
|
}
|
|
1811
1842
|
|
|
1812
|
-
snapToGuide(value) {
|
|
1843
|
+
#snapToGuide(value) {
|
|
1813
1844
|
if (!this.isShiftHeld) return value;
|
|
1814
1845
|
if (value < 0.1) return 0;
|
|
1815
1846
|
if (value > 0.9) return 1;
|
|
@@ -1817,7 +1848,7 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1817
1848
|
return value;
|
|
1818
1849
|
}
|
|
1819
1850
|
|
|
1820
|
-
snapToDiagonal(x, y) {
|
|
1851
|
+
#snapToDiagonal(x, y) {
|
|
1821
1852
|
if (!this.isShiftHeld) return { x, y };
|
|
1822
1853
|
const diff = Math.abs(x - y);
|
|
1823
1854
|
if (diff < 0.1) return { x: (x + y) / 2, y: (x + y) / 2 };
|
|
@@ -1830,25 +1861,22 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1830
1861
|
let x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
|
1831
1862
|
let y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
|
|
1832
1863
|
|
|
1833
|
-
|
|
1834
|
-
y =
|
|
1835
|
-
|
|
1836
|
-
x = this.snapToGuide(x);
|
|
1837
|
-
y = this.snapToGuide(y);
|
|
1864
|
+
x = this.#snapToGuide(x);
|
|
1865
|
+
y = this.#snapToGuide(y);
|
|
1838
1866
|
|
|
1839
|
-
const snapped = this
|
|
1867
|
+
const snapped = this.#snapToDiagonal(x, y);
|
|
1840
1868
|
this.position = snapped;
|
|
1841
|
-
this.value = [snapped.x, snapped.y];
|
|
1842
1869
|
|
|
1843
1870
|
this.cursor.style.left = `${snapped.x * 100}%`;
|
|
1844
|
-
this.cursor.style.top = `${
|
|
1845
|
-
if (this.text) {
|
|
1871
|
+
this.cursor.style.top = `${snapped.y * 100}%`; // Invert Y for display
|
|
1872
|
+
if (this.text && this.xInput && this.yInput) {
|
|
1846
1873
|
this.xInput.setAttribute("value", snapped.x.toFixed(3));
|
|
1847
1874
|
this.yInput.setAttribute("value", snapped.y.toFixed(3));
|
|
1848
1875
|
}
|
|
1849
1876
|
|
|
1850
1877
|
this.#emitInputEvent();
|
|
1851
1878
|
}
|
|
1879
|
+
|
|
1852
1880
|
#emitInputEvent() {
|
|
1853
1881
|
this.dispatchEvent(
|
|
1854
1882
|
new CustomEvent("input", {
|
|
@@ -1868,22 +1896,27 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1868
1896
|
}
|
|
1869
1897
|
|
|
1870
1898
|
#syncHandlePosition() {
|
|
1871
|
-
this.cursor
|
|
1872
|
-
|
|
1899
|
+
if (this.cursor) {
|
|
1900
|
+
this.cursor.style.left = `${this.position.x * 100}%`;
|
|
1901
|
+
this.cursor.style.top = `${this.position.y * 100}%`;
|
|
1902
|
+
}
|
|
1873
1903
|
}
|
|
1874
1904
|
|
|
1875
|
-
handleMouseDown(e) {
|
|
1905
|
+
#handleMouseDown(e) {
|
|
1876
1906
|
this.isDragging = true;
|
|
1907
|
+
|
|
1877
1908
|
this.#updatePosition(e);
|
|
1878
1909
|
|
|
1879
1910
|
this.plane.style.cursor = "grabbing";
|
|
1880
1911
|
|
|
1881
1912
|
const handleMouseMove = (e) => {
|
|
1913
|
+
this.plane.classList.add("dragging");
|
|
1882
1914
|
if (this.isDragging) this.#updatePosition(e);
|
|
1883
1915
|
};
|
|
1884
1916
|
|
|
1885
1917
|
const handleMouseUp = () => {
|
|
1886
1918
|
this.isDragging = false;
|
|
1919
|
+
this.plane.classList.remove("dragging");
|
|
1887
1920
|
this.plane.style.cursor = "";
|
|
1888
1921
|
window.removeEventListener("mousemove", handleMouseMove);
|
|
1889
1922
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
@@ -1894,13 +1927,296 @@ class FigInputJoystick extends HTMLElement {
|
|
|
1894
1927
|
window.addEventListener("mouseup", handleMouseUp);
|
|
1895
1928
|
}
|
|
1896
1929
|
|
|
1897
|
-
|
|
1930
|
+
#handleTouchStart(e) {
|
|
1931
|
+
e.preventDefault();
|
|
1932
|
+
this.isDragging = true;
|
|
1933
|
+
this.#updatePosition(e.touches[0]);
|
|
1934
|
+
|
|
1935
|
+
const handleTouchMove = (e) => {
|
|
1936
|
+
if (this.isDragging) this.#updatePosition(e.touches[0]);
|
|
1937
|
+
};
|
|
1938
|
+
|
|
1939
|
+
const handleTouchEnd = () => {
|
|
1940
|
+
this.isDragging = false;
|
|
1941
|
+
window.removeEventListener("touchmove", handleTouchMove);
|
|
1942
|
+
window.removeEventListener("touchend", handleTouchEnd);
|
|
1943
|
+
this.#emitChangeEvent();
|
|
1944
|
+
};
|
|
1945
|
+
|
|
1946
|
+
window.addEventListener("touchmove", handleTouchMove);
|
|
1947
|
+
window.addEventListener("touchend", handleTouchEnd);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
#handleKeyDown(e) {
|
|
1898
1951
|
if (e.key === "Shift") this.isShiftHeld = true;
|
|
1899
1952
|
}
|
|
1900
1953
|
|
|
1901
|
-
handleKeyUp(e) {
|
|
1954
|
+
#handleKeyUp(e) {
|
|
1902
1955
|
if (e.key === "Shift") this.isShiftHeld = false;
|
|
1903
1956
|
}
|
|
1957
|
+
static get observedAttributes() {
|
|
1958
|
+
return ["value", "precision", "transform", "text"];
|
|
1959
|
+
}
|
|
1960
|
+
get value() {
|
|
1961
|
+
return [this.position.x, this.position.y];
|
|
1962
|
+
}
|
|
1963
|
+
set value(value) {
|
|
1964
|
+
let v = value.toString().split(",").map(Number);
|
|
1965
|
+
this.position = { x: v[0], y: v[1] };
|
|
1966
|
+
this.#syncHandlePosition();
|
|
1967
|
+
}
|
|
1968
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1969
|
+
if (name === "value") {
|
|
1970
|
+
this.value = newValue;
|
|
1971
|
+
}
|
|
1972
|
+
if (name === "precision") {
|
|
1973
|
+
this.precision = parseInt(newValue);
|
|
1974
|
+
}
|
|
1975
|
+
if (name === "transform") {
|
|
1976
|
+
this.transform = Number(newValue);
|
|
1977
|
+
}
|
|
1978
|
+
if (name === "text" && newValue !== oldValue) {
|
|
1979
|
+
this.text = newValue.toLowerCase() === "true";
|
|
1980
|
+
this.#render();
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1904
1983
|
}
|
|
1905
1984
|
|
|
1906
1985
|
customElements.define("fig-input-joystick", FigInputJoystick);
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* A custom angle chooser input element.
|
|
1989
|
+
* @attr {number} value - The current angle of the handle in degrees.
|
|
1990
|
+
* @attr {number} precision - The number of decimal places for the output.
|
|
1991
|
+
* @attr {boolean} text - Whether to display a text input for the angle value.
|
|
1992
|
+
*/
|
|
1993
|
+
class FigInputAngle extends HTMLElement {
|
|
1994
|
+
constructor() {
|
|
1995
|
+
super();
|
|
1996
|
+
|
|
1997
|
+
this.angle = 0; // Angle in degrees
|
|
1998
|
+
this.isDragging = false;
|
|
1999
|
+
this.isShiftHeld = false;
|
|
2000
|
+
this.handle = null;
|
|
2001
|
+
this.angleInput = null;
|
|
2002
|
+
this.plane = null;
|
|
2003
|
+
|
|
2004
|
+
// Initialize position
|
|
2005
|
+
requestAnimationFrame(() => {
|
|
2006
|
+
this.precision = this.getAttribute("precision") || 1;
|
|
2007
|
+
this.precision = parseInt(this.precision);
|
|
2008
|
+
this.text = this.getAttribute("text") === "true";
|
|
2009
|
+
|
|
2010
|
+
this.#render();
|
|
2011
|
+
|
|
2012
|
+
this.#setupListeners();
|
|
2013
|
+
|
|
2014
|
+
this.#syncHandlePosition();
|
|
2015
|
+
if (this.text && this.angleInput) {
|
|
2016
|
+
this.angleInput.setAttribute(
|
|
2017
|
+
"value",
|
|
2018
|
+
this.angle.toFixed(this.precision)
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
disconnectedCallback() {
|
|
2025
|
+
this.#cleanupListeners();
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
#render() {
|
|
2029
|
+
this.innerHTML = this.#getInnerHTML();
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
#getInnerHTML() {
|
|
2033
|
+
return `
|
|
2034
|
+
<div class="fig-input-angle-plane">
|
|
2035
|
+
<div class="fig-input-angle-handle"></div>
|
|
2036
|
+
</div>
|
|
2037
|
+
${
|
|
2038
|
+
this.text
|
|
2039
|
+
? `<fig-input-text
|
|
2040
|
+
type="number"
|
|
2041
|
+
name="angle"
|
|
2042
|
+
step="0.1"
|
|
2043
|
+
value="${this.angle}"
|
|
2044
|
+
min="0"
|
|
2045
|
+
max="360">
|
|
2046
|
+
<span slot="append">°</span>
|
|
2047
|
+
</fig-input-text>`
|
|
2048
|
+
: ""
|
|
2049
|
+
}
|
|
2050
|
+
`;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
#setupListeners() {
|
|
2054
|
+
this.handle = this.querySelector(".fig-input-angle-handle");
|
|
2055
|
+
this.plane = this.querySelector(".fig-input-angle-plane");
|
|
2056
|
+
this.angleInput = this.querySelector("fig-input-text[name='angle']");
|
|
2057
|
+
this.plane.addEventListener("mousedown", this.#handleMouseDown.bind(this));
|
|
2058
|
+
this.plane.addEventListener(
|
|
2059
|
+
"touchstart",
|
|
2060
|
+
this.#handleTouchStart.bind(this)
|
|
2061
|
+
);
|
|
2062
|
+
window.addEventListener("keydown", this.#handleKeyDown.bind(this));
|
|
2063
|
+
window.addEventListener("keyup", this.#handleKeyUp.bind(this));
|
|
2064
|
+
if (this.text && this.angleInput) {
|
|
2065
|
+
this.angleInput = this.querySelector("fig-input-text");
|
|
2066
|
+
this.angleInput.addEventListener(
|
|
2067
|
+
"input",
|
|
2068
|
+
this.#handleAngleInput.bind(this)
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
#cleanupListeners() {
|
|
2074
|
+
this.plane.removeEventListener("mousedown", this.#handleMouseDown);
|
|
2075
|
+
this.plane.removeEventListener("touchstart", this.#handleTouchStart);
|
|
2076
|
+
window.removeEventListener("keydown", this.#handleKeyDown);
|
|
2077
|
+
window.removeEventListener("keyup", this.#handleKeyUp);
|
|
2078
|
+
if (this.text && this.angleInput) {
|
|
2079
|
+
this.angleInput.removeEventListener("input", this.#handleAngleInput);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
#handleAngleInput(e) {
|
|
2084
|
+
e.stopPropagation();
|
|
2085
|
+
this.angle = Number(e.target.value);
|
|
2086
|
+
this.#syncHandlePosition();
|
|
2087
|
+
this.#emitInputEvent();
|
|
2088
|
+
this.#emitChangeEvent();
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
#snapToIncrement(angle) {
|
|
2092
|
+
if (!this.isShiftHeld) return angle;
|
|
2093
|
+
const increment = 45;
|
|
2094
|
+
return Math.round(angle / increment) * increment;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
#updateAngle(e) {
|
|
2098
|
+
const rect = this.plane.getBoundingClientRect();
|
|
2099
|
+
const centerX = rect.left + rect.width / 2;
|
|
2100
|
+
const centerY = rect.top + rect.height / 2;
|
|
2101
|
+
const deltaX = e.clientX - centerX;
|
|
2102
|
+
const deltaY = e.clientY - centerY;
|
|
2103
|
+
let angle = ((Math.atan2(deltaY, deltaX) * 180) / Math.PI + 360) % 360;
|
|
2104
|
+
|
|
2105
|
+
angle = this.#snapToIncrement(angle);
|
|
2106
|
+
this.angle = angle;
|
|
2107
|
+
|
|
2108
|
+
this.#syncHandlePosition();
|
|
2109
|
+
if (this.text && this.angleInput) {
|
|
2110
|
+
this.angleInput.setAttribute("value", this.angle.toFixed(this.precision));
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
this.#emitInputEvent();
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
#emitInputEvent() {
|
|
2117
|
+
this.dispatchEvent(
|
|
2118
|
+
new CustomEvent("input", {
|
|
2119
|
+
bubbles: true,
|
|
2120
|
+
cancelable: true,
|
|
2121
|
+
})
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
#emitChangeEvent() {
|
|
2126
|
+
this.dispatchEvent(
|
|
2127
|
+
new CustomEvent("change", {
|
|
2128
|
+
bubbles: true,
|
|
2129
|
+
cancelable: true,
|
|
2130
|
+
})
|
|
2131
|
+
);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
#syncHandlePosition() {
|
|
2135
|
+
if (this.handle) {
|
|
2136
|
+
const radians = (this.angle * Math.PI) / 180;
|
|
2137
|
+
const radius = this.plane.offsetWidth / 2 - this.handle.offsetWidth / 2;
|
|
2138
|
+
const x = Math.cos(radians) * radius;
|
|
2139
|
+
const y = Math.sin(radians) * radius;
|
|
2140
|
+
this.handle.style.transform = `translate(${x}px, ${y}px)`;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
#handleMouseDown(e) {
|
|
2145
|
+
this.isDragging = true;
|
|
2146
|
+
this.#updateAngle(e);
|
|
2147
|
+
|
|
2148
|
+
const handleMouseMove = (e) => {
|
|
2149
|
+
if (this.isDragging) this.#updateAngle(e);
|
|
2150
|
+
};
|
|
2151
|
+
|
|
2152
|
+
const handleMouseUp = () => {
|
|
2153
|
+
this.isDragging = false;
|
|
2154
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
2155
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
2156
|
+
this.#emitChangeEvent();
|
|
2157
|
+
};
|
|
2158
|
+
|
|
2159
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
2160
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
#handleTouchStart(e) {
|
|
2164
|
+
e.preventDefault();
|
|
2165
|
+
this.isDragging = true;
|
|
2166
|
+
this.#updateAngle(e.touches[0]);
|
|
2167
|
+
|
|
2168
|
+
const handleTouchMove = (e) => {
|
|
2169
|
+
if (this.isDragging) this.#updateAngle(e.touches[0]);
|
|
2170
|
+
};
|
|
2171
|
+
|
|
2172
|
+
const handleTouchEnd = () => {
|
|
2173
|
+
this.isDragging = false;
|
|
2174
|
+
window.removeEventListener("touchmove", handleTouchMove);
|
|
2175
|
+
window.removeEventListener("touchend", handleTouchEnd);
|
|
2176
|
+
this.#emitChangeEvent();
|
|
2177
|
+
};
|
|
2178
|
+
|
|
2179
|
+
window.addEventListener("touchmove", handleTouchMove);
|
|
2180
|
+
window.addEventListener("touchend", handleTouchEnd);
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
#handleKeyDown(e) {
|
|
2184
|
+
if (e.key === "Shift") this.isShiftHeld = true;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
#handleKeyUp(e) {
|
|
2188
|
+
if (e.key === "Shift") this.isShiftHeld = false;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
static get observedAttributes() {
|
|
2192
|
+
return ["value", "precision", "text"];
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
get value() {
|
|
2196
|
+
return this.angle;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
set value(value) {
|
|
2200
|
+
if (isNaN(value)) {
|
|
2201
|
+
console.error("Invalid value: must be a number.");
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
this.angle = value;
|
|
2205
|
+
this.#syncHandlePosition();
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
2209
|
+
if (name === "value") {
|
|
2210
|
+
this.value = Number(newValue);
|
|
2211
|
+
}
|
|
2212
|
+
if (name === "precision") {
|
|
2213
|
+
this.precision = parseInt(newValue);
|
|
2214
|
+
}
|
|
2215
|
+
if (name === "text" && newValue !== oldValue) {
|
|
2216
|
+
this.text = newValue.toLowerCase() === "true";
|
|
2217
|
+
this.#render();
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
customElements.define("fig-input-angle", FigInputAngle);
|