@rogieking/figui3 3.23.0 → 4.0.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.
Files changed (4) hide show
  1. package/README.md +3 -4
  2. package/components.css +6 -21
  3. package/fig.js +200 -193
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1001,10 +1001,9 @@ An image display component with optional upload, aspect ratio, and object-fit co
1001
1001
  | Attribute | Type | Default | Description |
1002
1002
  |---|---|---|---|
1003
1003
  | `src` | string | — | Image URL |
1004
- | `upload` | boolean | `false` | Show upload button |
1005
- | `download` | boolean | `false` | Show download button |
1006
- | `label` | string | — | Upload button label |
1007
- | `aspect-ratio` | string | — | CSS aspect-ratio (e.g. `"16 / 9"`) |
1004
+ | `upload` | boolean | `false` | Show upload overlay (`fig-input-file`) |
1005
+ | `label` | string | `"Upload"` | Upload button label |
1006
+ | `aspect-ratio` | string | — | CSS aspect-ratio, or `"auto"` for lazy dimension detection |
1008
1007
  | `fit` | string | — | CSS object-fit (`"cover"`, `"contain"`, etc.) |
1009
1008
  | `checkerboard` | boolean | `false` | Show checkerboard behind transparent images |
1010
1009
 
package/components.css CHANGED
@@ -1395,8 +1395,12 @@ fig-image {
1395
1395
  opacity: 1;
1396
1396
  }
1397
1397
  }
1398
- &:not([src]):not([src=""]) fig-button,
1399
- &:hover fig-button {
1398
+ > fig-input-file[data-generated] {
1399
+ opacity: 0;
1400
+ pointer-events: none;
1401
+ }
1402
+ &:not([src]):not([src=""]) > fig-input-file[data-generated],
1403
+ &:hover > fig-input-file[data-generated] {
1400
1404
  opacity: 1;
1401
1405
  pointer-events: all;
1402
1406
  }
@@ -1420,25 +1424,6 @@ fig-image {
1420
1424
  aspect-ratio: var(--aspect-ratio) !important;
1421
1425
  }
1422
1426
  }
1423
- > div {
1424
- display: flex;
1425
- gap: var(--spacer-2);
1426
- opacity: 0;
1427
- pointer-events: none;
1428
- }
1429
- &:not([src]):not([src=""]) > div,
1430
- &:hover > div {
1431
- opacity: 1;
1432
- pointer-events: all;
1433
- }
1434
- fig-button[type="download"] {
1435
- display: none;
1436
- }
1437
- &[src]:not([src=""]) {
1438
- fig-button[type="download"] {
1439
- display: inline-flex;
1440
- }
1441
- }
1442
1427
  }
1443
1428
 
1444
1429
  /* Easing Curve */
package/fig.js CHANGED
@@ -8007,57 +8007,58 @@ customElements.define("fig-chit", FigChit);
8007
8007
  class FigSwatch extends FigChit {}
8008
8008
  customElements.define("fig-swatch", FigSwatch);
8009
8009
 
8010
- /* Upload */
8010
+ /* Image */
8011
8011
  /**
8012
- * A custom image upload element.
8013
- * @attr {string} src - The current image source URL
8014
- * @attr {boolean} upload - Whether to show the upload button
8015
- * @attr {string} label - The upload button label
8016
- * @attr {string} size - Size of the image preview
8012
+ * @attr {string} src - Image source URL
8013
+ * @attr {boolean} upload - Show upload overlay (generates fig-input-file)
8014
+ * @attr {string} label - Upload button label (default "Upload")
8015
+ * @attr {string} size - small | medium | large | auto
8016
+ * @attr {string} aspect-ratio - CSS aspect-ratio or "auto" (lazy dimension sniff)
8017
+ * @attr {string} fit - CSS object-fit value
8018
+ * @attr {boolean} checkerboard - Show checkerboard behind transparent images
8017
8019
  */
8018
8020
  class FigImage extends HTMLElement {
8019
8021
  #src = null;
8022
+ #chit = null;
8023
+ #fileInput = null;
8024
+ #blobUrl = null;
8025
+ #file = null;
8020
8026
  #boundHandleFileInput = this.#handleFileInput.bind(this);
8021
- #boundHandleDownload = this.#handleDownload.bind(this);
8022
- constructor() {
8023
- super();
8027
+
8028
+ static get observedAttributes() {
8029
+ return ["src", "upload", "aspect-ratio", "fit", "checkerboard"];
8024
8030
  }
8025
- #getInnerHTML() {
8026
- const cb =
8027
- this.hasAttribute("checkerboard") &&
8028
- this.getAttribute("checkerboard") !== "false";
8029
- const bg = this.src
8030
- ? `url(${this.src})`
8031
- : cb
8032
- ? "url()"
8033
- : "var(--figma-color-bg-secondary)";
8034
- return `<fig-chit size="large" data-type="image" background="${bg}" disabled${cb ? " checkerboard" : ""}></fig-chit><div>${
8035
- this.upload
8036
- ? `<fig-button variant="overlay" type="upload">
8037
- ${this.label}
8038
- <input type="file" accept="image/*" />
8039
- </fig-button>`
8040
- : ""
8041
- } ${
8042
- this.download
8043
- ? `<fig-button variant="overlay" icon="true" type="download">
8044
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
8045
- <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"/>
8046
- </svg></fig-button>`
8047
- : ""
8048
- }</div>`;
8031
+
8032
+ get src() {
8033
+ return this.#src;
8034
+ }
8035
+ set src(value) {
8036
+ this.#src = value;
8037
+ this.setAttribute("src", value);
8049
8038
  }
8039
+
8040
+ get file() {
8041
+ return this.#file;
8042
+ }
8043
+
8044
+ async getBase64() {
8045
+ const src = this.#src;
8046
+ if (!src) return null;
8047
+ const res = await fetch(src);
8048
+ const blob = await res.blob();
8049
+ const bitmap = await createImageBitmap(blob);
8050
+ const canvas = document.createElement("canvas");
8051
+ canvas.width = bitmap.width;
8052
+ canvas.height = bitmap.height;
8053
+ canvas.getContext("2d").drawImage(bitmap, 0, 0);
8054
+ bitmap.close();
8055
+ const dataUrl = canvas.toDataURL();
8056
+ return dataUrl;
8057
+ }
8058
+
8050
8059
  connectedCallback() {
8051
8060
  this.#src = this.getAttribute("src") || "";
8052
- this.upload =
8053
- this.hasAttribute("upload") && this.getAttribute("upload") !== "false";
8054
- this.download =
8055
- this.hasAttribute("download") &&
8056
- this.getAttribute("download") !== "false";
8057
- this.label = this.getAttribute("label") || "Upload";
8058
- this.size = this.getAttribute("size") || "small";
8059
- this.innerHTML = this.#getInnerHTML();
8060
- this.#updateRefs();
8061
+
8061
8062
  const ar = this.getAttribute("aspect-ratio");
8062
8063
  if (ar && ar !== "auto") {
8063
8064
  this.style.setProperty("--aspect-ratio", ar);
@@ -8066,182 +8067,169 @@ class FigImage extends HTMLElement {
8066
8067
  if (fit) {
8067
8068
  this.style.setProperty("--fit", fit);
8068
8069
  }
8070
+
8071
+ if (!this.querySelector("fig-chit")) {
8072
+ const chit = document.createElement("fig-chit");
8073
+ chit.setAttribute("data-generated", "");
8074
+ chit.setAttribute("size", "large");
8075
+ chit.setAttribute("data-type", "image");
8076
+ chit.setAttribute("disabled", "");
8077
+ this.#applyChitBackground(chit);
8078
+ if (this.hasAttribute("checkerboard") && this.getAttribute("checkerboard") !== "false") {
8079
+ chit.setAttribute("checkerboard", "");
8080
+ }
8081
+ this.prepend(chit);
8082
+ }
8083
+ this.#chit = this.querySelector("fig-chit");
8084
+
8085
+ const isUpload = this.hasAttribute("upload") && this.getAttribute("upload") !== "false";
8086
+ if (isUpload && !this.querySelector("fig-input-file[data-generated]")) {
8087
+ this.#createFileInput();
8088
+ }
8089
+
8090
+ if (this.#src && this.getAttribute("aspect-ratio") === "auto") {
8091
+ this.#sniffDimensions(this.#src);
8092
+ }
8069
8093
  }
8094
+
8070
8095
  disconnectedCallback() {
8071
- this.fileInput?.removeEventListener("change", this.#boundHandleFileInput);
8072
- this.downloadButton?.removeEventListener(
8073
- "click",
8074
- this.#boundHandleDownload,
8075
- );
8096
+ this.#fileInput?.removeEventListener("change", this.#boundHandleFileInput);
8097
+ if (this.#blobUrl) {
8098
+ URL.revokeObjectURL(this.#blobUrl);
8099
+ this.#blobUrl = null;
8100
+ }
8076
8101
  }
8077
8102
 
8078
- #updateRefs() {
8079
- requestAnimationFrame(() => {
8080
- this.chit = this.querySelector("fig-chit");
8081
- if (this.upload) {
8082
- this.uploadButton = this.querySelector("fig-button[type='upload']");
8083
- this.fileInput = this.uploadButton?.querySelector("input");
8084
- this.fileInput?.removeEventListener(
8085
- "change",
8086
- this.#boundHandleFileInput,
8087
- );
8088
- this.fileInput?.addEventListener("change", this.#boundHandleFileInput);
8089
- }
8090
- if (this.download) {
8091
- this.downloadButton = this.querySelector("fig-button[type='download']");
8092
- this.downloadButton?.removeEventListener(
8093
- "click",
8094
- this.#boundHandleDownload,
8095
- );
8096
- this.downloadButton?.addEventListener(
8097
- "click",
8098
- this.#boundHandleDownload,
8099
- );
8100
- }
8101
- });
8103
+ #applyChitBackground(chit) {
8104
+ const cb = this.hasAttribute("checkerboard") && this.getAttribute("checkerboard") !== "false";
8105
+ if (this.#src) {
8106
+ chit.setAttribute("background", `url(${this.#src})`);
8107
+ } else {
8108
+ chit.setAttribute("background", cb ? "url()" : "var(--figma-color-bg-secondary)");
8109
+ }
8102
8110
  }
8103
- #handleDownload() {
8104
- //force blob download
8105
- const link = document.createElement("a");
8106
- link.href = this.blob;
8107
- link.download = "image.png";
8108
- link.click();
8109
- }
8110
- async #loadImage(src) {
8111
- // Get blob from canvas
8112
- await new Promise((resolve) => {
8113
- this.image = new Image();
8114
- this.image.crossOrigin = "Anonymous";
8115
- this.image.onload = async () => {
8116
- this.aspectRatio = this.image.width / this.image.height;
8117
- const ar = this.getAttribute("aspect-ratio");
8118
- if (!ar || ar === "auto") {
8119
- this.style.setProperty(
8120
- "--aspect-ratio",
8121
- `${this.image.width}/${this.image.height}`,
8122
- );
8123
- }
8124
- this.dispatchEvent(
8125
- new CustomEvent("loaded", {
8126
- bubbles: true,
8127
- cancelable: true,
8128
- detail: {
8129
- blob: this.blob,
8130
- base64: this.base64,
8131
- },
8132
- }),
8133
- );
8134
- resolve();
8135
-
8136
- // Create canvas to extract blob and base64 from image
8137
- const canvas = document.createElement("canvas");
8138
- const ctx = canvas.getContext("2d");
8139
- canvas.width = this.image.width;
8140
- canvas.height = this.image.height;
8141
- ctx.drawImage(this.image, 0, 0);
8142
-
8143
- // Get base64 from canvas
8144
- this.base64 = canvas.toDataURL();
8145
-
8146
- // Get blob from canvas
8147
- canvas.toBlob((blob) => {
8148
- if (this.blob) {
8149
- URL.revokeObjectURL(this.blob);
8150
- }
8151
- if (blob) {
8152
- this.blob = URL.createObjectURL(blob);
8153
- }
8154
- });
8155
- };
8156
- this.image.src = src;
8157
- });
8111
+
8112
+ #createFileInput() {
8113
+ const fi = document.createElement("fig-input-file");
8114
+ fi.setAttribute("data-generated", "");
8115
+ fi.setAttribute("accepts", "image/*");
8116
+ fi.setAttribute("variant", "overlay");
8117
+ const defaultLabel = this.getAttribute("label") || "Upload";
8118
+ fi.setAttribute("label", this.#src ? "Replace" : defaultLabel);
8119
+ if (this.#src) fi.setAttribute("url", this.#src);
8120
+ fi.addEventListener("change", this.#boundHandleFileInput);
8121
+ this.append(fi);
8122
+ this.#fileInput = fi;
8158
8123
  }
8159
- async #handleFileInput(e) {
8160
- if (this.blob) {
8161
- URL.revokeObjectURL(this.blob);
8162
- }
8163
- this.blob = URL.createObjectURL(e.target.files[0]);
8164
- //set base64 url
8165
- const reader = new FileReader();
8166
- reader.readAsDataURL(e.target.files[0]);
8167
- //await this data url to be set
8168
- await new Promise((resolve) => {
8169
- reader.onload = (e) => {
8170
- this.base64 = e.target.result;
8171
- resolve();
8172
- };
8173
- });
8174
- //emit event for loaded
8124
+
8125
+ #removeFileInput() {
8126
+ if (this.#fileInput) {
8127
+ this.#fileInput.removeEventListener("change", this.#boundHandleFileInput);
8128
+ this.#fileInput.remove();
8129
+ this.#fileInput = null;
8130
+ }
8131
+ }
8132
+
8133
+ #handleFileInput(e) {
8134
+ const file = e.detail?.files?.[0];
8135
+
8136
+ if (!file) {
8137
+ if (this.#blobUrl) {
8138
+ URL.revokeObjectURL(this.#blobUrl);
8139
+ this.#blobUrl = null;
8140
+ }
8141
+ this.#file = null;
8142
+ this.removeAttribute("src");
8143
+ this.dispatchEvent(
8144
+ new CustomEvent("change", { bubbles: true, cancelable: true }),
8145
+ );
8146
+ return;
8147
+ }
8148
+
8149
+ if (this.#blobUrl) {
8150
+ URL.revokeObjectURL(this.#blobUrl);
8151
+ }
8152
+ this.#file = file;
8153
+ this.#blobUrl = URL.createObjectURL(file);
8154
+
8155
+ this.setAttribute("src", this.#blobUrl);
8156
+
8175
8157
  this.dispatchEvent(
8176
8158
  new CustomEvent("loaded", {
8177
8159
  bubbles: true,
8178
8160
  cancelable: true,
8179
- detail: {
8180
- blob: this.blob,
8181
- base64: this.base64,
8182
- },
8161
+ detail: { file, src: this.#blobUrl },
8183
8162
  }),
8184
8163
  );
8185
- //emit for change too
8186
8164
  this.dispatchEvent(
8187
- new CustomEvent("change", {
8188
- bubbles: true,
8189
- cancelable: true,
8190
- }),
8165
+ new CustomEvent("change", { bubbles: true, cancelable: true }),
8191
8166
  );
8192
- this.setAttribute("src", this.blob);
8193
- }
8194
- static get observedAttributes() {
8195
- return ["src", "upload", "download", "aspect-ratio", "fit", "checkerboard"];
8196
- }
8197
- get src() {
8198
- return this.#src;
8167
+
8168
+ if (this.#fileInput) {
8169
+ this.#fileInput.clear();
8170
+ this.#fileInput.setAttribute("label", "Replace");
8171
+ }
8199
8172
  }
8200
- set src(value) {
8201
- this.#src = value;
8202
- this.setAttribute("src", value);
8173
+
8174
+ async #sniffDimensions(src) {
8175
+ try {
8176
+ let blob;
8177
+ if (src.startsWith("blob:")) {
8178
+ const res = await fetch(src);
8179
+ blob = await res.blob();
8180
+ } else {
8181
+ const res = await fetch(src, { mode: "cors" });
8182
+ blob = await res.blob();
8183
+ }
8184
+ const bitmap = await createImageBitmap(blob);
8185
+ this.style.setProperty("--aspect-ratio", `${bitmap.width}/${bitmap.height}`);
8186
+ bitmap.close();
8187
+ } catch {
8188
+ // Non-critical — CSS aspect-ratio fallback handles it
8189
+ }
8203
8190
  }
8204
8191
 
8205
8192
  attributeChangedCallback(name, oldValue, newValue) {
8193
+ if (oldValue === newValue) return;
8194
+
8206
8195
  if (name === "src") {
8207
8196
  this.#src = newValue;
8208
- if (this.chit) {
8209
- const hasCb =
8210
- this.hasAttribute("checkerboard") &&
8211
- this.getAttribute("checkerboard") !== "false";
8197
+ if (this.#chit) {
8198
+ this.#applyChitBackground(this.#chit);
8199
+ }
8200
+ if (this.#fileInput) {
8201
+ const defaultLabel = this.getAttribute("label") || "Upload";
8202
+ this.#fileInput.setAttribute("label", this.#src ? "Replace" : defaultLabel);
8212
8203
  if (this.#src) {
8213
- this.chit.setAttribute("background", `url(${this.#src})`);
8204
+ this.#fileInput.setAttribute("url", this.#src);
8214
8205
  } else {
8215
- this.chit.setAttribute(
8216
- "background",
8217
- hasCb ? "url()" : "var(--figma-color-bg-secondary)",
8218
- );
8206
+ this.#fileInput.removeAttribute("url");
8219
8207
  }
8220
8208
  }
8221
- if (this.#src) {
8222
- this.#loadImage(this.#src);
8209
+ if (this.#src && this.getAttribute("aspect-ratio") === "auto") {
8210
+ this.#sniffDimensions(this.#src);
8223
8211
  }
8224
8212
  }
8213
+
8225
8214
  if (name === "upload") {
8226
- this.upload = newValue !== null && newValue !== "false";
8227
- this.innerHTML = this.#getInnerHTML();
8228
- this.#updateRefs();
8229
- }
8230
- if (name === "download") {
8231
- this.download = newValue !== null && newValue !== "false";
8232
- this.innerHTML = this.#getInnerHTML();
8233
- this.#updateRefs();
8234
- }
8235
- if (name === "size") {
8236
- this.size = newValue;
8215
+ const on = newValue !== null && newValue !== "false";
8216
+ if (on && !this.#fileInput) {
8217
+ this.#createFileInput();
8218
+ } else if (!on) {
8219
+ this.#removeFileInput();
8220
+ }
8237
8221
  }
8222
+
8238
8223
  if (name === "aspect-ratio") {
8239
8224
  if (newValue && newValue !== "auto") {
8240
8225
  this.style.setProperty("--aspect-ratio", newValue);
8241
8226
  } else if (!newValue) {
8242
8227
  this.style.removeProperty("--aspect-ratio");
8228
+ } else if (newValue === "auto" && this.#src) {
8229
+ this.#sniffDimensions(this.#src);
8243
8230
  }
8244
8231
  }
8232
+
8245
8233
  if (name === "fit") {
8246
8234
  if (newValue) {
8247
8235
  this.style.setProperty("--fit", newValue);
@@ -8249,12 +8237,13 @@ class FigImage extends HTMLElement {
8249
8237
  this.style.removeProperty("--fit");
8250
8238
  }
8251
8239
  }
8240
+
8252
8241
  if (name === "checkerboard") {
8253
- if (this.chit) {
8242
+ if (this.#chit) {
8254
8243
  if (newValue !== null && newValue !== "false") {
8255
- this.chit.setAttribute("checkerboard", "");
8244
+ this.#chit.setAttribute("checkerboard", "");
8256
8245
  } else {
8257
- this.chit.removeAttribute("checkerboard");
8246
+ this.#chit.removeAttribute("checkerboard");
8258
8247
  }
8259
8248
  }
8260
8249
  }
@@ -8264,7 +8253,7 @@ customElements.define("fig-image", FigImage);
8264
8253
 
8265
8254
  /* File Upload Input */
8266
8255
  class FigInputFile extends HTMLElement {
8267
- static observedAttributes = ["accepts", "label", "disabled", "multiple"];
8256
+ static observedAttributes = ["accepts", "label", "disabled", "multiple", "variant", "url"];
8268
8257
 
8269
8258
  #fileInput = null;
8270
8259
  #filenameEl = null;
@@ -8277,10 +8266,24 @@ class FigInputFile extends HTMLElement {
8277
8266
  return this.#files;
8278
8267
  }
8279
8268
 
8269
+ get #urlFilename() {
8270
+ const url = this.getAttribute("url");
8271
+ if (!url) return "";
8272
+ try {
8273
+ const path = new URL(url, location.href).pathname;
8274
+ const name = path.split("/").pop();
8275
+ return name ? decodeURIComponent(name) : url;
8276
+ } catch {
8277
+ return url;
8278
+ }
8279
+ }
8280
+
8280
8281
  get value() {
8281
- if (!this.#files || this.#files.length === 0) return "";
8282
- if (this.#files.length === 1) return this.#files[0].name;
8283
- return `${this.#files.length} files`;
8282
+ if (this.#files && this.#files.length > 0) {
8283
+ if (this.#files.length === 1) return this.#files[0].name;
8284
+ return `${this.#files.length} files`;
8285
+ }
8286
+ return this.#urlFilename;
8284
8287
  }
8285
8288
 
8286
8289
  connectedCallback() {
@@ -8306,6 +8309,7 @@ class FigInputFile extends HTMLElement {
8306
8309
  clear() {
8307
8310
  this.#files = null;
8308
8311
  if (this.#fileInput) this.#fileInput.value = "";
8312
+ this.removeAttribute("url");
8309
8313
  this.#render();
8310
8314
  this.#emitEvents();
8311
8315
  }
@@ -8319,6 +8323,7 @@ class FigInputFile extends HTMLElement {
8319
8323
  #onFileChange = () => {
8320
8324
  if (this.#fileInput.files.length > 0) {
8321
8325
  this.#files = this.#fileInput.files;
8326
+ this.removeAttribute("url");
8322
8327
  this.#render();
8323
8328
  this.#emitEvents();
8324
8329
  }
@@ -8373,6 +8378,7 @@ class FigInputFile extends HTMLElement {
8373
8378
  if (this.#fileInput) {
8374
8379
  this.#fileInput.files = dt.files;
8375
8380
  }
8381
+ this.removeAttribute("url");
8376
8382
  this.#render();
8377
8383
  this.#emitEvents();
8378
8384
  };
@@ -8384,7 +8390,8 @@ class FigInputFile extends HTMLElement {
8384
8390
  this.hasAttribute("disabled") &&
8385
8391
  this.getAttribute("disabled") !== "false";
8386
8392
  const multiple = this.hasAttribute("multiple");
8387
- const hasFile = this.#files && this.#files.length > 0;
8393
+ const variant = this.getAttribute("variant") || "input";
8394
+ const hasFile = (this.#files && this.#files.length > 0) || !!this.getAttribute("url");
8388
8395
 
8389
8396
  this.innerHTML = "";
8390
8397
 
@@ -8397,7 +8404,7 @@ class FigInputFile extends HTMLElement {
8397
8404
  : "";
8398
8405
 
8399
8406
  this.#uploadBtn = document.createElement("fig-button");
8400
- this.#uploadBtn.setAttribute("variant", "input");
8407
+ this.#uploadBtn.setAttribute("variant", variant);
8401
8408
  this.#uploadBtn.setAttribute("type", "upload");
8402
8409
  this.#uploadBtn.className = "fig-input-file-filename";
8403
8410
  if (disabled) this.#uploadBtn.setAttribute("disabled", "");
@@ -8452,7 +8459,7 @@ class FigInputFile extends HTMLElement {
8452
8459
  }
8453
8460
 
8454
8461
  this.#uploadBtn = document.createElement("fig-button");
8455
- this.#uploadBtn.setAttribute("variant", "input");
8462
+ this.#uploadBtn.setAttribute("variant", variant);
8456
8463
  this.#uploadBtn.setAttribute("type", "upload");
8457
8464
  this.#uploadBtn.textContent = label;
8458
8465
  if (disabled) this.#uploadBtn.setAttribute("disabled", "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "3.23.0",
3
+ "version": "4.0.0",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "MIT",