@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 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("pointerleave", this.hidePopup.bind(this));
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="primary" type="upload">
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "1.7.7",
3
+ "version": "1.8.0",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "devDependencies": {