@rogieking/figui3 1.7.6 → 1.7.9

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
@@ -421,6 +421,7 @@
421
421
  --handle-shadow: 0px 0 0 0.75px rgba(0, 0, 0, 0.05),
422
422
  0px 1px 3px 0px rgba(0, 0, 0, 0.05), 0px 3px 8px 0px rgba(0, 0, 0, 0.05),
423
423
  0px 0px 0.5px 0px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
424
+ --handle-color: #fff;
424
425
 
425
426
  --popover-min-width: 15rem;
426
427
  --figma-color-bg-ghost-hover: rgba(0, 0, 0, 0.05);
@@ -770,6 +771,17 @@ fig-button {
770
771
  color: var(--figma-color-text);
771
772
  }
772
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
+
773
785
  /* Icon only */
774
786
  &[icon] {
775
787
  width: var(--spacer-4);
@@ -1229,10 +1241,6 @@ fig-image {
1229
1241
  fig-chit {
1230
1242
  --size: var(--image-size) !important;
1231
1243
  }
1232
- fig-button {
1233
- opacity: 0;
1234
- pointer-events: none;
1235
- }
1236
1244
  &:not([src]):not([src=""]) fig-button,
1237
1245
  &:hover fig-button {
1238
1246
  opacity: 1;
@@ -1254,6 +1262,25 @@ fig-image {
1254
1262
  aspect-ratio: var(--aspect-ratio) !important;
1255
1263
  }
1256
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
+ }
1257
1284
  }
1258
1285
 
1259
1286
  /* Combo input */
@@ -1311,7 +1338,7 @@ input[type="checkbox"].switch {
1311
1338
 
1312
1339
  input[type="checkbox"].switch:after {
1313
1340
  content: "";
1314
- background-color: var(--figma-color-icon-onbrand);
1341
+ background-color: var(--handle-color);
1315
1342
  width: calc(1rem - 2px);
1316
1343
  height: calc(1rem - 2px);
1317
1344
  border-radius: 0.5rem;
@@ -1553,15 +1580,14 @@ details {
1553
1580
  fig-slider {
1554
1581
  --slider-field-height: 1.5rem;
1555
1582
  --slider-height: 1rem;
1556
- --slider-handle-color: #fff;
1557
1583
  --slider-thumb-size: var(--slider-height);
1558
1584
  --slider-percent: calc(var(--slider-complete) * 100%);
1559
1585
  --start-percent: calc(var(--default, 0) * 100%);
1560
1586
  --slider-tick-size: calc(var(--slider-height) / 4);
1561
1587
  --slider-handle-shadow: inset 0 0 0 calc(4px + 0.5rem * var(--unchanged))
1562
- var(--slider-handle-color),
1588
+ var(--handle-color),
1563
1589
  inset 0 0 0 5px rgba(0, 0, 0, 0.1), var(--figma-elevation-200);
1564
- --slider-handle-shadow-focus: inset 0 0 0 4px var(--slider-handle-color),
1590
+ --slider-handle-shadow-focus: inset 0 0 0 4px var(--handle-color),
1565
1591
  inset 0 0 0 5px rgba(0, 0, 0, 0.1), 0px 0 0 0.75px rgba(0, 0, 0, 0.1),
1566
1592
  0px 1px 3px 0px rgba(0, 0, 0, 0.1), 0px 3px 8px 0px rgba(0, 0, 0, 0.1),
1567
1593
  0px 0px 0.5px 0px rgba(0, 0, 0, 0.18),
@@ -1842,7 +1868,7 @@ fig-slider {
1842
1868
  --handle-transition: none;
1843
1869
  --slider-transition: none;
1844
1870
  --slider-handle-shadow: inset 0 0 0 calc(6px + 0.5rem * var(--unchanged))
1845
- var(--slider-handle-color),
1871
+ var(--handle-color),
1846
1872
  0 0 0 0.75px rgba(0, 0, 0, 0.075), inset 0 0 0 5px rgba(0, 0, 0, 0.1),
1847
1873
  var(--figma-elevation-200);
1848
1874
 
@@ -2391,7 +2417,6 @@ fig-segmented-control {
2391
2417
 
2392
2418
  fig-input-joystick {
2393
2419
  --size: 1.5rem;
2394
- --joystick-handle-color: #fff;
2395
2420
  display: inline-flex;
2396
2421
  gap: var(--spacer-2);
2397
2422
  user-select: none;
@@ -2481,7 +2506,7 @@ fig-input-joystick {
2481
2506
  z-index: 1;
2482
2507
  width: 0.5rem;
2483
2508
  height: 0.5rem;
2484
- background: var(--joystick-handle-color);
2509
+ background: var(--handle-color);
2485
2510
  box-shadow: var(--handle-shadow);
2486
2511
  border-radius: 50%;
2487
2512
  transform: translate(-50%, -50%);
@@ -2490,7 +2515,6 @@ fig-input-joystick {
2490
2515
 
2491
2516
  fig-input-angle {
2492
2517
  --size: 1.5rem;
2493
- --handle-color: #fff;
2494
2518
  display: inline-flex;
2495
2519
  gap: var(--spacer-2);
2496
2520
  user-select: none;
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>
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,15 @@ 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("touchend", this.#handleTouchEnd);
256
+ } else if (this.action === "click") {
257
+ this.removeEventListener("touchstart", this.showDelayedPopup);
258
+ }
248
259
  }
249
260
 
250
261
  setup() {
@@ -286,12 +297,25 @@ class FigTooltip extends HTMLElement {
286
297
  this.addEventListener("pointerleave", this.hidePopup.bind(this));
287
298
  // Add mousedown listener instead of dragstart
288
299
  this.addEventListener("mousedown", this.#boundHideOnDragStart);
300
+
301
+ // Touch support for mobile hover simulation
302
+ this.addEventListener("touchstart", this.#handleTouchStart.bind(this), {
303
+ passive: true,
304
+ });
305
+ this.addEventListener("touchend", this.#handleTouchEnd.bind(this), {
306
+ passive: true,
307
+ });
289
308
  } else if (this.action === "click") {
290
309
  this.addEventListener("click", this.showDelayedPopup.bind(this));
291
310
  document.body.addEventListener(
292
311
  "click",
293
312
  this.hidePopupOutsideClick.bind(this)
294
313
  );
314
+
315
+ // Touch support for better mobile responsiveness
316
+ this.addEventListener("touchstart", this.showDelayedPopup.bind(this), {
317
+ passive: true,
318
+ });
295
319
  }
296
320
 
297
321
  // Add listener for chrome interactions
@@ -352,6 +376,7 @@ class FigTooltip extends HTMLElement {
352
376
 
353
377
  hidePopup() {
354
378
  clearTimeout(this.timeout);
379
+ clearTimeout(this.#touchTimeout);
355
380
  this.popup.style.opacity = "0";
356
381
  this.popup.style.display = "block";
357
382
  this.popup.style.pointerEvents = "none";
@@ -364,6 +389,27 @@ class FigTooltip extends HTMLElement {
364
389
  this.hidePopup();
365
390
  }
366
391
  }
392
+
393
+ // Touch event handlers for mobile support
394
+ #handleTouchStart(event) {
395
+ if (this.action === "hover") {
396
+ this.#isTouching = true;
397
+ // Show popup on touch start for hover action
398
+ this.showDelayedPopup();
399
+ }
400
+ }
401
+
402
+ #handleTouchEnd(event) {
403
+ if (this.action === "hover" && this.#isTouching) {
404
+ this.#isTouching = false;
405
+ // Hide popup after a short delay to allow for taps
406
+ clearTimeout(this.#touchTimeout);
407
+ this.#touchTimeout = setTimeout(() => {
408
+ this.hidePopup();
409
+ }, 100);
410
+ }
411
+ }
412
+
367
413
  static get observedAttributes() {
368
414
  return ["action", "delay", "open"];
369
415
  }
@@ -1836,22 +1882,29 @@ class FigImage extends HTMLElement {
1836
1882
  #getInnerHTML() {
1837
1883
  return `<fig-chit type="image" size="large" ${
1838
1884
  this.src ? `src="${this.src}"` : ""
1839
- } disabled="true"></fig-chit>${
1885
+ } disabled="true"></fig-chit><div>${
1840
1886
  this.upload
1841
- ? `<fig-button variant="primary" type="upload">
1887
+ ? `<fig-button variant="overlay" type="upload">
1842
1888
  ${this.label}
1843
1889
  <input type="file" accept="image/*" />
1844
1890
  </fig-button>`
1845
1891
  : ""
1846
- }`;
1892
+ } ${
1893
+ this.download
1894
+ ? `<fig-button variant="overlay" icon="true" type="download">
1895
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1896
+ <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"/>
1897
+ </svg></fig-button>`
1898
+ : ""
1899
+ }</div>`;
1847
1900
  }
1848
1901
  connectedCallback() {
1849
1902
  this.src = this.getAttribute("src") || "";
1850
1903
  this.upload = this.getAttribute("upload") === "true";
1904
+ this.download = this.getAttribute("download") === "true";
1851
1905
  this.label = this.getAttribute("label") || "Upload";
1852
1906
  this.size = this.getAttribute("size") || "small";
1853
1907
  this.innerHTML = this.#getInnerHTML();
1854
- this.#updateRefs();
1855
1908
  }
1856
1909
  disconnectedCallback() {
1857
1910
  this.fileInput.removeEventListener(
@@ -1864,16 +1917,38 @@ class FigImage extends HTMLElement {
1864
1917
  requestAnimationFrame(() => {
1865
1918
  this.chit = this.querySelector("fig-chit");
1866
1919
  if (this.upload) {
1867
- this.uploadButton = this.querySelector("fig-button");
1920
+ this.uploadButton = this.querySelector("fig-button[type='upload']");
1868
1921
  this.fileInput = this.uploadButton?.querySelector("input");
1869
-
1922
+ this.fileInput.removeEventListener(
1923
+ "change",
1924
+ this.#handleFileInput.bind(this)
1925
+ );
1870
1926
  this.fileInput.addEventListener(
1871
1927
  "change",
1872
1928
  this.#handleFileInput.bind(this)
1873
1929
  );
1874
1930
  }
1931
+ if (this.download) {
1932
+ console.log("binding download");
1933
+ this.downloadButton = this.querySelector("fig-button[type='download']");
1934
+ this.downloadButton.removeEventListener(
1935
+ "click",
1936
+ this.#handleDownload.bind(this)
1937
+ );
1938
+ this.downloadButton.addEventListener(
1939
+ "click",
1940
+ this.#handleDownload.bind(this)
1941
+ );
1942
+ }
1875
1943
  });
1876
1944
  }
1945
+ #handleDownload() {
1946
+ //force blob download
1947
+ const link = document.createElement("a");
1948
+ link.href = this.blob;
1949
+ link.download = "image.png";
1950
+ link.click();
1951
+ }
1877
1952
  async #loadImage(src) {
1878
1953
  // Get blob from canvas
1879
1954
  await new Promise((resolve) => {
@@ -1967,8 +2042,9 @@ class FigImage extends HTMLElement {
1967
2042
  this.#loadImage(this.#src);
1968
2043
  }
1969
2044
  }
1970
- if (name === "upload") {
2045
+ if (name === "upload" || name === "download") {
1971
2046
  this.upload = newValue.toLowerCase() === "true";
2047
+ this.download = newValue.toLowerCase() === "true";
1972
2048
  this.innerHTML = this.#getInnerHTML();
1973
2049
  this.#updateRefs();
1974
2050
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "1.7.6",
3
+ "version": "1.7.9",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "devDependencies": {