@rogieking/figui3 1.7.7 → 1.8.0
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 +30 -30
- package/example.html +8 -0
- package/fig.js +124 -8
- package/package.json +1 -1
package/components.css
CHANGED
|
@@ -771,6 +771,17 @@ fig-button {
|
|
|
771
771
|
color: var(--figma-color-text);
|
|
772
772
|
}
|
|
773
773
|
|
|
774
|
+
/* Variant: Overlay (for on top of content) */
|
|
775
|
+
&[variant="overlay"] {
|
|
776
|
+
background-color: var(--figma-color-bg);
|
|
777
|
+
color: var(--figma-color-text);
|
|
778
|
+
box-shadow: 0 0 0 1px var(--figma-color-bordertranslucent);
|
|
779
|
+
|
|
780
|
+
&:active {
|
|
781
|
+
background-color: var(--figma-color-bg-secondary);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
774
785
|
/* Icon only */
|
|
775
786
|
&[icon] {
|
|
776
787
|
width: var(--spacer-4);
|
|
@@ -1230,10 +1241,6 @@ fig-image {
|
|
|
1230
1241
|
fig-chit {
|
|
1231
1242
|
--size: var(--image-size) !important;
|
|
1232
1243
|
}
|
|
1233
|
-
fig-button {
|
|
1234
|
-
opacity: 0;
|
|
1235
|
-
pointer-events: none;
|
|
1236
|
-
}
|
|
1237
1244
|
&:not([src]):not([src=""]) fig-button,
|
|
1238
1245
|
&:hover fig-button {
|
|
1239
1246
|
opacity: 1;
|
|
@@ -1255,6 +1262,25 @@ fig-image {
|
|
|
1255
1262
|
aspect-ratio: var(--aspect-ratio) !important;
|
|
1256
1263
|
}
|
|
1257
1264
|
}
|
|
1265
|
+
> div {
|
|
1266
|
+
display: flex;
|
|
1267
|
+
gap: var(--spacer-2);
|
|
1268
|
+
opacity: 0;
|
|
1269
|
+
pointer-events: none;
|
|
1270
|
+
}
|
|
1271
|
+
&:not([src]):not([src=""]) > div,
|
|
1272
|
+
&:hover > div {
|
|
1273
|
+
opacity: 1;
|
|
1274
|
+
pointer-events: all;
|
|
1275
|
+
}
|
|
1276
|
+
fig-button[type="download"] {
|
|
1277
|
+
display: none;
|
|
1278
|
+
}
|
|
1279
|
+
&[src]:not([src=""]) {
|
|
1280
|
+
fig-button[type="download"] {
|
|
1281
|
+
display: inline-flex;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1258
1284
|
}
|
|
1259
1285
|
|
|
1260
1286
|
/* Combo input */
|
|
@@ -1851,20 +1877,6 @@ fig-slider {
|
|
|
1851
1877
|
position: relative;
|
|
1852
1878
|
display: block;
|
|
1853
1879
|
width: 100%;
|
|
1854
|
-
box-shadow: none;
|
|
1855
|
-
background: var(--figma-color-bg-tertiary);
|
|
1856
|
-
|
|
1857
|
-
/* Track */
|
|
1858
|
-
&::before {
|
|
1859
|
-
box-shadow: none;
|
|
1860
|
-
background: var(--figma-color-text-tertiary);
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
input[type="range"] {
|
|
1864
|
-
&::-webkit-slider-runnable-track {
|
|
1865
|
-
box-shadow: none;
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
1880
|
}
|
|
1869
1881
|
fig-input-text {
|
|
1870
1882
|
height: calc(var(--slider-height) * 2);
|
|
@@ -1873,18 +1885,6 @@ fig-slider {
|
|
|
1873
1885
|
|
|
1874
1886
|
&:hover,
|
|
1875
1887
|
&:focus-within {
|
|
1876
|
-
.fig-slider-input-container {
|
|
1877
|
-
background: var(--figma-color-bg-secondary);
|
|
1878
|
-
&::before {
|
|
1879
|
-
box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
|
|
1880
|
-
background: var(--figma-color-bg-brand);
|
|
1881
|
-
}
|
|
1882
|
-
}
|
|
1883
|
-
input[type="range"] {
|
|
1884
|
-
&::-webkit-slider-runnable-track {
|
|
1885
|
-
box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
1888
|
fig-input-text {
|
|
1889
1889
|
height: auto;
|
|
1890
1890
|
}
|
package/example.html
CHANGED
|
@@ -280,6 +280,7 @@
|
|
|
280
280
|
<fig-field>
|
|
281
281
|
<label>Large (with upload)</label>
|
|
282
282
|
<fig-image upload="true"
|
|
283
|
+
download="true"
|
|
283
284
|
label="Upload image"
|
|
284
285
|
size="large"></fig-image>
|
|
285
286
|
</fig-field>
|
|
@@ -492,6 +493,7 @@
|
|
|
492
493
|
is="fig-dialog">
|
|
493
494
|
<fig-header>
|
|
494
495
|
<h3>Dialog</h3>
|
|
496
|
+
|
|
495
497
|
<fig-button variant="ghost"
|
|
496
498
|
icon="true"
|
|
497
499
|
close-dialog>
|
|
@@ -510,6 +512,12 @@
|
|
|
510
512
|
</fig-button>
|
|
511
513
|
</fig-header>
|
|
512
514
|
<fig-content>
|
|
515
|
+
<fig-tooltip text="Close dialog">
|
|
516
|
+
<fig-button variant="ghost"
|
|
517
|
+
close-dialog>
|
|
518
|
+
Close
|
|
519
|
+
</fig-button>
|
|
520
|
+
</fig-tooltip>
|
|
513
521
|
<p>Some content here</p>
|
|
514
522
|
<fig-field direction="horizontal">
|
|
515
523
|
<label for="colorLevels">Color Levels</label>
|
package/fig.js
CHANGED
|
@@ -220,6 +220,8 @@ customElements.define("fig-dropdown", FigDropdown);
|
|
|
220
220
|
class FigTooltip extends HTMLElement {
|
|
221
221
|
#boundHideOnChromeOpen;
|
|
222
222
|
#boundHideOnDragStart;
|
|
223
|
+
#touchTimeout;
|
|
224
|
+
#isTouching = false;
|
|
223
225
|
constructor() {
|
|
224
226
|
super();
|
|
225
227
|
this.action = this.getAttribute("action") || "hover";
|
|
@@ -245,6 +247,17 @@ class FigTooltip extends HTMLElement {
|
|
|
245
247
|
);
|
|
246
248
|
// Remove mousedown listener
|
|
247
249
|
this.removeEventListener("mousedown", this.#boundHideOnDragStart);
|
|
250
|
+
|
|
251
|
+
// Clean up touch-related timers and listeners
|
|
252
|
+
clearTimeout(this.#touchTimeout);
|
|
253
|
+
if (this.action === "hover") {
|
|
254
|
+
this.removeEventListener("touchstart", this.#handleTouchStart);
|
|
255
|
+
this.removeEventListener("touchmove", this.#handleTouchMove);
|
|
256
|
+
this.removeEventListener("touchend", this.#handleTouchEnd);
|
|
257
|
+
this.removeEventListener("touchcancel", this.#handleTouchCancel);
|
|
258
|
+
} else if (this.action === "click") {
|
|
259
|
+
this.removeEventListener("touchstart", this.showDelayedPopup);
|
|
260
|
+
}
|
|
248
261
|
}
|
|
249
262
|
|
|
250
263
|
setup() {
|
|
@@ -283,15 +296,37 @@ class FigTooltip extends HTMLElement {
|
|
|
283
296
|
setupEventListeners() {
|
|
284
297
|
if (this.action === "hover") {
|
|
285
298
|
this.addEventListener("pointerenter", this.showDelayedPopup.bind(this));
|
|
286
|
-
this.addEventListener(
|
|
299
|
+
this.addEventListener(
|
|
300
|
+
"pointerleave",
|
|
301
|
+
this.#handlePointerLeave.bind(this)
|
|
302
|
+
);
|
|
287
303
|
// Add mousedown listener instead of dragstart
|
|
288
304
|
this.addEventListener("mousedown", this.#boundHideOnDragStart);
|
|
305
|
+
|
|
306
|
+
// Touch support for mobile hover simulation
|
|
307
|
+
this.addEventListener("touchstart", this.#handleTouchStart.bind(this), {
|
|
308
|
+
passive: true,
|
|
309
|
+
});
|
|
310
|
+
this.addEventListener("touchmove", this.#handleTouchMove.bind(this), {
|
|
311
|
+
passive: true,
|
|
312
|
+
});
|
|
313
|
+
this.addEventListener("touchend", this.#handleTouchEnd.bind(this), {
|
|
314
|
+
passive: true,
|
|
315
|
+
});
|
|
316
|
+
this.addEventListener("touchcancel", this.#handleTouchCancel.bind(this), {
|
|
317
|
+
passive: true,
|
|
318
|
+
});
|
|
289
319
|
} else if (this.action === "click") {
|
|
290
320
|
this.addEventListener("click", this.showDelayedPopup.bind(this));
|
|
291
321
|
document.body.addEventListener(
|
|
292
322
|
"click",
|
|
293
323
|
this.hidePopupOutsideClick.bind(this)
|
|
294
324
|
);
|
|
325
|
+
|
|
326
|
+
// Touch support for better mobile responsiveness
|
|
327
|
+
this.addEventListener("touchstart", this.showDelayedPopup.bind(this), {
|
|
328
|
+
passive: true,
|
|
329
|
+
});
|
|
295
330
|
}
|
|
296
331
|
|
|
297
332
|
// Add listener for chrome interactions
|
|
@@ -352,6 +387,7 @@ class FigTooltip extends HTMLElement {
|
|
|
352
387
|
|
|
353
388
|
hidePopup() {
|
|
354
389
|
clearTimeout(this.timeout);
|
|
390
|
+
clearTimeout(this.#touchTimeout);
|
|
355
391
|
this.popup.style.opacity = "0";
|
|
356
392
|
this.popup.style.display = "block";
|
|
357
393
|
this.popup.style.pointerEvents = "none";
|
|
@@ -364,6 +400,56 @@ class FigTooltip extends HTMLElement {
|
|
|
364
400
|
this.hidePopup();
|
|
365
401
|
}
|
|
366
402
|
}
|
|
403
|
+
|
|
404
|
+
// Pointer event handlers
|
|
405
|
+
#handlePointerLeave(event) {
|
|
406
|
+
// Don't hide immediately if we're in a touch interaction
|
|
407
|
+
if (!this.#isTouching) {
|
|
408
|
+
this.hidePopup();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Touch event handlers for mobile support
|
|
413
|
+
#handleTouchStart(event) {
|
|
414
|
+
if (this.action === "hover") {
|
|
415
|
+
this.#isTouching = true;
|
|
416
|
+
// Clear any existing touch timeout
|
|
417
|
+
clearTimeout(this.#touchTimeout);
|
|
418
|
+
// Show popup on touch start for hover action
|
|
419
|
+
this.showDelayedPopup();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
#handleTouchMove(event) {
|
|
424
|
+
if (this.action === "hover" && this.#isTouching) {
|
|
425
|
+
// If user is scrolling/moving, cancel the tooltip after a delay
|
|
426
|
+
clearTimeout(this.#touchTimeout);
|
|
427
|
+
this.#touchTimeout = setTimeout(() => {
|
|
428
|
+
this.#isTouching = false;
|
|
429
|
+
this.hidePopup();
|
|
430
|
+
}, 150);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
#handleTouchEnd(event) {
|
|
435
|
+
if (this.action === "hover" && this.#isTouching) {
|
|
436
|
+
// Delay setting isTouching to false to prevent pointerleave from hiding immediately
|
|
437
|
+
clearTimeout(this.#touchTimeout);
|
|
438
|
+
this.#touchTimeout = setTimeout(() => {
|
|
439
|
+
this.#isTouching = false;
|
|
440
|
+
this.hidePopup();
|
|
441
|
+
}, 300); // Increased delay for better mobile UX
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
#handleTouchCancel(event) {
|
|
446
|
+
if (this.action === "hover" && this.#isTouching) {
|
|
447
|
+
this.#isTouching = false;
|
|
448
|
+
clearTimeout(this.#touchTimeout);
|
|
449
|
+
this.hidePopup();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
367
453
|
static get observedAttributes() {
|
|
368
454
|
return ["action", "delay", "open"];
|
|
369
455
|
}
|
|
@@ -1836,22 +1922,29 @@ class FigImage extends HTMLElement {
|
|
|
1836
1922
|
#getInnerHTML() {
|
|
1837
1923
|
return `<fig-chit type="image" size="large" ${
|
|
1838
1924
|
this.src ? `src="${this.src}"` : ""
|
|
1839
|
-
} disabled="true"></fig-chit>${
|
|
1925
|
+
} disabled="true"></fig-chit><div>${
|
|
1840
1926
|
this.upload
|
|
1841
|
-
? `<fig-button variant="
|
|
1927
|
+
? `<fig-button variant="overlay" type="upload">
|
|
1842
1928
|
${this.label}
|
|
1843
1929
|
<input type="file" accept="image/*" />
|
|
1844
1930
|
</fig-button>`
|
|
1845
1931
|
: ""
|
|
1846
|
-
}
|
|
1932
|
+
} ${
|
|
1933
|
+
this.download
|
|
1934
|
+
? `<fig-button variant="overlay" icon="true" type="download">
|
|
1935
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1936
|
+
<path d="M17.5 13C17.7761 13 18 13.2239 18 13.5V16.5C18 17.3284 17.3284 18 16.5 18H7.5C6.67157 18 6 17.3284 6 16.5V13.5C6 13.2239 6.22386 13 6.5 13C6.77614 13 7 13.2239 7 13.5V16.5C7 16.7761 7.22386 17 7.5 17H16.5C16.7761 17 17 16.7761 17 16.5V13.5C17 13.2239 17.2239 13 17.5 13ZM12 6C12.2761 6 12.5 6.22386 12.5 6.5V12.293L14.6465 10.1465C14.8417 9.95122 15.1583 9.95122 15.3535 10.1465C15.5488 10.3417 15.5488 10.6583 15.3535 10.8535L12.3535 13.8535C12.2597 13.9473 12.1326 14 12 14C11.9006 14 11.8042 13.9704 11.7227 13.916L11.6465 13.8535L8.64648 10.8535C8.45122 10.6583 8.45122 10.3417 8.64648 10.1465C8.84175 9.95122 9.15825 9.95122 9.35352 10.1465L11.5 12.293V6.5C11.5 6.22386 11.7239 6 12 6Z" fill="black"/>
|
|
1937
|
+
</svg></fig-button>`
|
|
1938
|
+
: ""
|
|
1939
|
+
}</div>`;
|
|
1847
1940
|
}
|
|
1848
1941
|
connectedCallback() {
|
|
1849
1942
|
this.src = this.getAttribute("src") || "";
|
|
1850
1943
|
this.upload = this.getAttribute("upload") === "true";
|
|
1944
|
+
this.download = this.getAttribute("download") === "true";
|
|
1851
1945
|
this.label = this.getAttribute("label") || "Upload";
|
|
1852
1946
|
this.size = this.getAttribute("size") || "small";
|
|
1853
1947
|
this.innerHTML = this.#getInnerHTML();
|
|
1854
|
-
this.#updateRefs();
|
|
1855
1948
|
}
|
|
1856
1949
|
disconnectedCallback() {
|
|
1857
1950
|
this.fileInput.removeEventListener(
|
|
@@ -1864,16 +1957,38 @@ class FigImage extends HTMLElement {
|
|
|
1864
1957
|
requestAnimationFrame(() => {
|
|
1865
1958
|
this.chit = this.querySelector("fig-chit");
|
|
1866
1959
|
if (this.upload) {
|
|
1867
|
-
this.uploadButton = this.querySelector("fig-button");
|
|
1960
|
+
this.uploadButton = this.querySelector("fig-button[type='upload']");
|
|
1868
1961
|
this.fileInput = this.uploadButton?.querySelector("input");
|
|
1869
|
-
|
|
1962
|
+
this.fileInput.removeEventListener(
|
|
1963
|
+
"change",
|
|
1964
|
+
this.#handleFileInput.bind(this)
|
|
1965
|
+
);
|
|
1870
1966
|
this.fileInput.addEventListener(
|
|
1871
1967
|
"change",
|
|
1872
1968
|
this.#handleFileInput.bind(this)
|
|
1873
1969
|
);
|
|
1874
1970
|
}
|
|
1971
|
+
if (this.download) {
|
|
1972
|
+
console.log("binding download");
|
|
1973
|
+
this.downloadButton = this.querySelector("fig-button[type='download']");
|
|
1974
|
+
this.downloadButton.removeEventListener(
|
|
1975
|
+
"click",
|
|
1976
|
+
this.#handleDownload.bind(this)
|
|
1977
|
+
);
|
|
1978
|
+
this.downloadButton.addEventListener(
|
|
1979
|
+
"click",
|
|
1980
|
+
this.#handleDownload.bind(this)
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1875
1983
|
});
|
|
1876
1984
|
}
|
|
1985
|
+
#handleDownload() {
|
|
1986
|
+
//force blob download
|
|
1987
|
+
const link = document.createElement("a");
|
|
1988
|
+
link.href = this.blob;
|
|
1989
|
+
link.download = "image.png";
|
|
1990
|
+
link.click();
|
|
1991
|
+
}
|
|
1877
1992
|
async #loadImage(src) {
|
|
1878
1993
|
// Get blob from canvas
|
|
1879
1994
|
await new Promise((resolve) => {
|
|
@@ -1967,8 +2082,9 @@ class FigImage extends HTMLElement {
|
|
|
1967
2082
|
this.#loadImage(this.#src);
|
|
1968
2083
|
}
|
|
1969
2084
|
}
|
|
1970
|
-
if (name === "upload") {
|
|
2085
|
+
if (name === "upload" || name === "download") {
|
|
1971
2086
|
this.upload = newValue.toLowerCase() === "true";
|
|
2087
|
+
this.download = newValue.toLowerCase() === "true";
|
|
1972
2088
|
this.innerHTML = this.#getInnerHTML();
|
|
1973
2089
|
this.#updateRefs();
|
|
1974
2090
|
}
|