@rogieking/figui3 3.4.3 → 3.6.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/components.css +237 -32
  2. package/dist/fig.js +29 -29
  3. package/fig.js +399 -81
  4. package/package.json +1 -1
package/fig.js CHANGED
@@ -9,7 +9,7 @@ function figIsWebKitOrIOSBrowser() {
9
9
  const userAgent = navigator.userAgent || "";
10
10
  const isIOSBrowser =
11
11
  /\b(iPad|iPhone|iPod)\b/.test(userAgent) ||
12
- /\bMacintosh\b/.test(userAgent) && /\bMobile\b/.test(userAgent);
12
+ (/\bMacintosh\b/.test(userAgent) && /\bMobile\b/.test(userAgent));
13
13
  const isDesktopWebKit =
14
14
  /\bAppleWebKit\b/.test(userAgent) &&
15
15
  !/\b(Chrome|Chromium|Edg|OPR|SamsungBrowser)\b/.test(userAgent);
@@ -41,9 +41,10 @@ function figSupportsCustomizedBuiltIns() {
41
41
 
42
42
  const figNeedsBuiltInPolyfill =
43
43
  figIsWebKitOrIOSBrowser() && !figSupportsCustomizedBuiltIns();
44
- const figBuiltInPolyfillReady = (figNeedsBuiltInPolyfill
45
- ? import("./polyfills/custom-elements-webkit.js")
46
- : Promise.resolve()
44
+ const figBuiltInPolyfillReady = (
45
+ figNeedsBuiltInPolyfill
46
+ ? import("./polyfills/custom-elements-webkit.js")
47
+ : Promise.resolve()
47
48
  )
48
49
  .then(() => {})
49
50
  .catch((error) => {
@@ -62,14 +63,12 @@ function figDefineCustomizedBuiltIn(name, constructor, options) {
62
63
  return;
63
64
  }
64
65
 
65
- figBuiltInPolyfillReady
66
- .then(define)
67
- .catch((error) => {
68
- console.error(
69
- `[figui3] Failed to load customized built-in polyfill for "${name}".`,
70
- error,
71
- );
72
- });
66
+ figBuiltInPolyfillReady.then(define).catch((error) => {
67
+ console.error(
68
+ `[figui3] Failed to load customized built-in polyfill for "${name}".`,
69
+ error,
70
+ );
71
+ });
73
72
  }
74
73
 
75
74
  function figUniqueId() {
@@ -1208,18 +1207,25 @@ class FigPopup extends HTMLDialogElement {
1208
1207
  }
1209
1208
 
1210
1209
  ensureInitialized() {
1211
- if (typeof this._anchorObserver === "undefined") this._anchorObserver = null;
1212
- if (typeof this._contentObserver === "undefined") this._contentObserver = null;
1213
- if (typeof this._mutationObserver === "undefined") this._mutationObserver = null;
1214
- if (typeof this._anchorTrackRAF === "undefined") this._anchorTrackRAF = null;
1215
- if (typeof this._lastAnchorRect === "undefined") this._lastAnchorRect = null;
1210
+ if (typeof this._anchorObserver === "undefined")
1211
+ this._anchorObserver = null;
1212
+ if (typeof this._contentObserver === "undefined")
1213
+ this._contentObserver = null;
1214
+ if (typeof this._mutationObserver === "undefined")
1215
+ this._mutationObserver = null;
1216
+ if (typeof this._anchorTrackRAF === "undefined")
1217
+ this._anchorTrackRAF = null;
1218
+ if (typeof this._lastAnchorRect === "undefined")
1219
+ this._lastAnchorRect = null;
1216
1220
  if (typeof this._isPopupActive === "undefined") this._isPopupActive = false;
1217
1221
  if (typeof this._rafId === "undefined") this._rafId = null;
1218
1222
  if (typeof this._anchorRef === "undefined") this._anchorRef = null;
1219
1223
  if (typeof this._isDragging === "undefined") this._isDragging = false;
1220
1224
  if (typeof this._dragPending === "undefined") this._dragPending = false;
1221
- if (typeof this._dragStartPos === "undefined") this._dragStartPos = { x: 0, y: 0 };
1222
- if (typeof this._dragOffset === "undefined") this._dragOffset = { x: 0, y: 0 };
1225
+ if (typeof this._dragStartPos === "undefined")
1226
+ this._dragStartPos = { x: 0, y: 0 };
1227
+ if (typeof this._dragOffset === "undefined")
1228
+ this._dragOffset = { x: 0, y: 0 };
1223
1229
  if (typeof this._dragThreshold !== "number") this._dragThreshold = 3;
1224
1230
  if (typeof this._wasDragged === "undefined") this._wasDragged = false;
1225
1231
 
@@ -1228,7 +1234,11 @@ class FigPopup extends HTMLDialogElement {
1228
1234
  }
1229
1235
  if (typeof this._boundScroll !== "function") {
1230
1236
  this._boundScroll = (e) => {
1231
- if (this.open && !this.contains(e.target) && this.shouldAutoReposition()) {
1237
+ if (
1238
+ this.open &&
1239
+ !this.contains(e.target) &&
1240
+ this.shouldAutoReposition()
1241
+ ) {
1232
1242
  this.queueReposition();
1233
1243
  }
1234
1244
  };
@@ -1516,7 +1526,10 @@ class FigPopup extends HTMLDialogElement {
1516
1526
  const anchor = this.resolveAnchor();
1517
1527
  const nextRect = this.readRectSnapshot(anchor);
1518
1528
  const canAutoReposition = this.shouldAutoReposition();
1519
- if (canAutoReposition && this.hasRectChanged(this._lastAnchorRect, nextRect)) {
1529
+ if (
1530
+ canAutoReposition &&
1531
+ this.hasRectChanged(this._lastAnchorRect, nextRect)
1532
+ ) {
1520
1533
  this._lastAnchorRect = nextRect;
1521
1534
  this.queueReposition();
1522
1535
  } else if (!canAutoReposition) {
@@ -1989,7 +2002,9 @@ class FigPopup extends HTMLDialogElement {
1989
2002
  const anchorCenterY = anchorRect.top + anchorRect.height / 2;
1990
2003
  const measuredRect = this.getBoundingClientRect();
1991
2004
  const rect =
1992
- measuredRect.width > 0 && measuredRect.height > 0 ? measuredRect : popupRect;
2005
+ measuredRect.width > 0 && measuredRect.height > 0
2006
+ ? measuredRect
2007
+ : popupRect;
1993
2008
  // Always use the rendered popup rect so beak alignment matches real final placement.
1994
2009
  const resolvedLeft = rect.left;
1995
2010
  const resolvedTop = rect.top;
@@ -2082,14 +2097,7 @@ class FigPopup extends HTMLDialogElement {
2082
2097
  let bestScore = Number.POSITIVE_INFINITY;
2083
2098
 
2084
2099
  for (const { v, h, s } of candidates) {
2085
- const coords = this.computeCoords(
2086
- anchorRect,
2087
- popupRect,
2088
- v,
2089
- h,
2090
- offset,
2091
- s,
2092
- );
2100
+ const coords = this.computeCoords(anchorRect, popupRect, v, h, offset, s);
2093
2101
  const placementSide = this.getPlacementSide(v, h, s);
2094
2102
 
2095
2103
  if (s) {
@@ -3102,11 +3110,7 @@ class FigSlider extends HTMLElement {
3102
3110
  break;
3103
3111
  case "default":
3104
3112
  this.default =
3105
- newValue !== null
3106
- ? newValue
3107
- : this.type === "delta"
3108
- ? 0
3109
- : this.min;
3113
+ newValue !== null ? newValue : this.type === "delta" ? 0 : this.min;
3110
3114
  this.#syncProperties();
3111
3115
  break;
3112
3116
  case "min":
@@ -3172,7 +3176,8 @@ class FigInputText extends HTMLElement {
3172
3176
  this.placeholder = this.getAttribute("placeholder") || "";
3173
3177
  this.name = this.getAttribute("name") || null;
3174
3178
  this.readonly =
3175
- this.hasAttribute("readonly") && this.getAttribute("readonly") !== "false";
3179
+ this.hasAttribute("readonly") &&
3180
+ this.getAttribute("readonly") !== "false";
3176
3181
 
3177
3182
  if (this.type === "number") {
3178
3183
  if (this.getAttribute("step")) {
@@ -4007,6 +4012,229 @@ class FigField extends HTMLElement {
4007
4012
  }
4008
4013
  customElements.define("fig-field", FigField);
4009
4014
 
4015
+ /* Field + Slider wrapper */
4016
+ class FigFieldSlider extends HTMLElement {
4017
+ #field = null;
4018
+ #label = null;
4019
+ #slider = null;
4020
+ #observer = null;
4021
+ #managedSliderAttrs = new Set();
4022
+ #boundHandleSliderInput = null;
4023
+ #boundHandleSliderChange = null;
4024
+ #ignoredSliderAttrs = new Set(["variant", "color", "text", "full"]);
4025
+
4026
+ static get observedAttributes() {
4027
+ return ["label", "direction"];
4028
+ }
4029
+
4030
+ connectedCallback() {
4031
+ if (!this.#field) {
4032
+ this.#initialize();
4033
+ }
4034
+
4035
+ this.#syncField();
4036
+ this.#syncSliderAttributes();
4037
+ this.#bindSliderEvents();
4038
+
4039
+ if (!this.#observer) {
4040
+ this.#observer = new MutationObserver((mutations) => {
4041
+ let syncField = false;
4042
+ let syncSlider = false;
4043
+
4044
+ for (const mutation of mutations) {
4045
+ if (mutation.type === "attributes") {
4046
+ if (
4047
+ mutation.attributeName &&
4048
+ this.#ignoredSliderAttrs.has(mutation.attributeName)
4049
+ ) {
4050
+ continue;
4051
+ }
4052
+ if (
4053
+ mutation.attributeName === "label" ||
4054
+ mutation.attributeName === "direction"
4055
+ ) {
4056
+ syncField = true;
4057
+ } else {
4058
+ syncSlider = true;
4059
+ }
4060
+ }
4061
+ }
4062
+
4063
+ if (syncField) this.#syncField();
4064
+ if (syncSlider) this.#syncSliderAttributes();
4065
+ });
4066
+ }
4067
+
4068
+ this.#observer.observe(this, { attributes: true });
4069
+ }
4070
+
4071
+ disconnectedCallback() {
4072
+ this.#observer?.disconnect();
4073
+ this.#unbindSliderEvents();
4074
+ }
4075
+
4076
+ attributeChangedCallback(name, oldValue, newValue) {
4077
+ if (oldValue === newValue || !this.#field) return;
4078
+ if (name === "label" || name === "direction") {
4079
+ this.#syncField();
4080
+ }
4081
+ }
4082
+
4083
+ #initialize() {
4084
+ const initialChildren = Array.from(this.childNodes).filter((node) => {
4085
+ return (
4086
+ node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim())
4087
+ );
4088
+ });
4089
+
4090
+ const field = document.createElement("fig-field");
4091
+ const label = document.createElement("label");
4092
+ const slider = document.createElement("fig-slider");
4093
+ slider.setAttribute("text", "true");
4094
+ for (const attrName of this.#getForwardedSliderAttrNames()) {
4095
+ const value = this.getAttribute(attrName);
4096
+ slider.setAttribute(attrName, value ?? "");
4097
+ }
4098
+
4099
+ field.append(label, slider);
4100
+
4101
+ this.#field = field;
4102
+ this.#label = label;
4103
+ this.#slider = slider;
4104
+
4105
+ this.replaceChildren(field);
4106
+
4107
+ for (const node of initialChildren) {
4108
+ this.#slider.appendChild(node);
4109
+ }
4110
+ }
4111
+
4112
+ #syncField() {
4113
+ if (!this.#field || !this.#label) return;
4114
+ const hasLabelAttr = this.hasAttribute("label");
4115
+ const rawLabel = this.getAttribute("label");
4116
+ const isBlankLabel = hasLabelAttr && (rawLabel ?? "").trim() === "";
4117
+
4118
+ if (isBlankLabel) {
4119
+ if (this.#label.parentElement === this.#field) {
4120
+ this.#label.remove();
4121
+ }
4122
+ } else {
4123
+ this.#label.textContent = hasLabelAttr ? rawLabel ?? "" : "Label";
4124
+ if (this.#label.parentElement !== this.#field) {
4125
+ this.#field.prepend(this.#label);
4126
+ }
4127
+ }
4128
+
4129
+ this.#field.setAttribute(
4130
+ "direction",
4131
+ this.getAttribute("direction") || "horizontal",
4132
+ );
4133
+ }
4134
+
4135
+ #syncSliderAttributes() {
4136
+ if (!this.#slider) return;
4137
+ const hostAttrs = this.#getForwardedSliderAttrNames();
4138
+
4139
+ const nextManaged = new Set(hostAttrs.filter((name) => name !== "text"));
4140
+
4141
+ for (const attrName of this.#managedSliderAttrs) {
4142
+ if (!nextManaged.has(attrName)) {
4143
+ this.#slider.removeAttribute(attrName);
4144
+ }
4145
+ }
4146
+
4147
+ for (const attrName of hostAttrs) {
4148
+ if (attrName === "text") continue;
4149
+ const value = this.getAttribute(attrName);
4150
+ this.#slider.setAttribute(attrName, value ?? "");
4151
+ }
4152
+
4153
+ this.#slider.removeAttribute("variant");
4154
+ this.#slider.removeAttribute("color");
4155
+ this.#slider.removeAttribute("transform");
4156
+ this.#slider.removeAttribute("full");
4157
+ this.#slider.setAttribute("text", "true");
4158
+
4159
+ const sliderType = (this.getAttribute("type") || "range").toLowerCase();
4160
+ if (sliderType === "delta" || sliderType === "stepper") {
4161
+ this.#slider.setAttribute(
4162
+ "default",
4163
+ this.getAttribute("default") ?? "50",
4164
+ );
4165
+ } else if (!this.hasAttribute("default")) {
4166
+ this.#slider.removeAttribute("default");
4167
+ }
4168
+ if (sliderType === "stepper") {
4169
+ this.#slider.setAttribute("step", this.getAttribute("step") ?? "10");
4170
+ } else if (!this.hasAttribute("step")) {
4171
+ this.#slider.removeAttribute("step");
4172
+ }
4173
+ if (sliderType === "opacity") {
4174
+ this.#slider.style.setProperty(
4175
+ "--color",
4176
+ "var(--figma-color-bg-tertiary)",
4177
+ );
4178
+ } else {
4179
+ this.#slider.style.removeProperty("--color");
4180
+ }
4181
+
4182
+ this.#managedSliderAttrs = nextManaged;
4183
+ }
4184
+
4185
+ #getForwardedSliderAttrNames() {
4186
+ const reserved = new Set(["label", "direction", "oninput", "onchange"]);
4187
+ return this.getAttributeNames().filter(
4188
+ (name) => !reserved.has(name) && !this.#ignoredSliderAttrs.has(name),
4189
+ );
4190
+ }
4191
+
4192
+ #bindSliderEvents() {
4193
+ if (!this.#slider) return;
4194
+ if (!this.#boundHandleSliderInput) {
4195
+ this.#boundHandleSliderInput = this.#forwardSliderEvent.bind(
4196
+ this,
4197
+ "input",
4198
+ );
4199
+ }
4200
+ if (!this.#boundHandleSliderChange) {
4201
+ this.#boundHandleSliderChange = this.#forwardSliderEvent.bind(
4202
+ this,
4203
+ "change",
4204
+ );
4205
+ }
4206
+ this.#slider.addEventListener("input", this.#boundHandleSliderInput);
4207
+ this.#slider.addEventListener("change", this.#boundHandleSliderChange);
4208
+ }
4209
+
4210
+ #unbindSliderEvents() {
4211
+ if (!this.#slider) return;
4212
+ if (this.#boundHandleSliderInput) {
4213
+ this.#slider.removeEventListener("input", this.#boundHandleSliderInput);
4214
+ }
4215
+ if (this.#boundHandleSliderChange) {
4216
+ this.#slider.removeEventListener("change", this.#boundHandleSliderChange);
4217
+ }
4218
+ }
4219
+
4220
+ #forwardSliderEvent(type, event) {
4221
+ event.stopPropagation();
4222
+ const detail =
4223
+ event instanceof CustomEvent && event.detail !== undefined
4224
+ ? event.detail
4225
+ : this.#slider?.value;
4226
+ this.dispatchEvent(
4227
+ new CustomEvent(type, {
4228
+ detail,
4229
+ bubbles: true,
4230
+ cancelable: true,
4231
+ composed: true,
4232
+ }),
4233
+ );
4234
+ }
4235
+ }
4236
+ customElements.define("fig-field-slider", FigFieldSlider);
4237
+
4010
4238
  /* Color swatch */
4011
4239
  class FigInputColor extends HTMLElement {
4012
4240
  rgba;
@@ -5684,8 +5912,22 @@ class FigChit extends HTMLElement {
5684
5912
  }
5685
5913
  }
5686
5914
 
5915
+ #resolveBackground(bg) {
5916
+ if (!bg || !bg.includes("var(")) return bg;
5917
+ const prev = this.style.background;
5918
+ this.style.background = bg;
5919
+ const cs = getComputedStyle(this);
5920
+ const bgImage = cs.backgroundImage;
5921
+ const bgColor = cs.backgroundColor;
5922
+ this.style.background = prev;
5923
+ if (bgImage && bgImage !== "none") return bgImage;
5924
+ return bgColor || bg;
5925
+ }
5926
+
5687
5927
  #render() {
5688
- const bg = this.getAttribute("background") || "#D9D9D9";
5928
+ const rawBg = this.getAttribute("background") || "#D9D9D9";
5929
+ const isVar = rawBg.includes("var(");
5930
+ const bg = isVar ? this.#resolveBackground(rawBg) : rawBg;
5689
5931
  const newType = this.#detectType(bg);
5690
5932
 
5691
5933
  // Only rebuild DOM if type changes
@@ -5702,21 +5944,22 @@ class FigChit extends HTMLElement {
5702
5944
  const hex = this.#toHex(bg);
5703
5945
  this.innerHTML = `<input type="color" value="${hex}" />`;
5704
5946
  this.input = this.querySelector("input");
5705
- this.input.addEventListener("input", this.#boundHandleInput);
5947
+ if (!isVar) {
5948
+ this.input.addEventListener("input", this.#boundHandleInput);
5949
+ }
5706
5950
  } else {
5707
5951
  this.innerHTML = "";
5708
5952
  this.input = null;
5709
5953
  }
5710
5954
  } else if (this.#type === "color" && this.input) {
5711
- // Just update input value without rebuilding DOM
5712
5955
  const hex = this.#toHex(bg);
5713
5956
  if (this.input.value !== hex) {
5714
5957
  this.input.value = hex;
5715
5958
  }
5716
5959
  }
5717
5960
 
5718
- // Always update CSS variable
5719
- this.style.setProperty("--chit-background", bg);
5961
+ // Always update CSS variable with raw value so vars stay reactive
5962
+ this.style.setProperty("--chit-background", rawBg);
5720
5963
  }
5721
5964
 
5722
5965
  #handleInput(e) {
@@ -5783,9 +6026,15 @@ class FigImage extends HTMLElement {
5783
6026
  super();
5784
6027
  }
5785
6028
  #getInnerHTML() {
5786
- return `<fig-chit size="large" background="${
5787
- this.src ? `url(${this.src})` : "url()"
5788
- }" disabled></fig-chit><div>${
6029
+ const cb =
6030
+ this.hasAttribute("checkerboard") &&
6031
+ this.getAttribute("checkerboard") !== "false";
6032
+ const bg = this.src
6033
+ ? `url(${this.src})`
6034
+ : cb
6035
+ ? "url()"
6036
+ : "var(--figma-color-bg-secondary)";
6037
+ return `<fig-chit size="large" data-type="image" background="${bg}" disabled${cb ? " checkerboard" : ""}></fig-chit><div>${
5789
6038
  this.upload
5790
6039
  ? `<fig-button variant="overlay" type="upload">
5791
6040
  ${this.label}
@@ -5946,7 +6195,7 @@ class FigImage extends HTMLElement {
5946
6195
  this.setAttribute("src", this.blob);
5947
6196
  }
5948
6197
  static get observedAttributes() {
5949
- return ["src", "upload", "download", "aspect-ratio", "fit"];
6198
+ return ["src", "upload", "download", "aspect-ratio", "fit", "checkerboard"];
5950
6199
  }
5951
6200
  get src() {
5952
6201
  return this.#src;
@@ -5960,10 +6209,17 @@ class FigImage extends HTMLElement {
5960
6209
  if (name === "src") {
5961
6210
  this.#src = newValue;
5962
6211
  if (this.chit) {
5963
- this.chit.setAttribute(
5964
- "background",
5965
- this.#src ? `url(${this.#src})` : "",
5966
- );
6212
+ const hasCb =
6213
+ this.hasAttribute("checkerboard") &&
6214
+ this.getAttribute("checkerboard") !== "false";
6215
+ if (this.#src) {
6216
+ this.chit.setAttribute("background", `url(${this.#src})`);
6217
+ } else {
6218
+ this.chit.setAttribute(
6219
+ "background",
6220
+ hasCb ? "url()" : "var(--figma-color-bg-secondary)",
6221
+ );
6222
+ }
5967
6223
  }
5968
6224
  if (this.#src) {
5969
6225
  this.#loadImage(this.#src);
@@ -5996,6 +6252,15 @@ class FigImage extends HTMLElement {
5996
6252
  this.style.removeProperty("--fit");
5997
6253
  }
5998
6254
  }
6255
+ if (name === "checkerboard") {
6256
+ if (this.chit) {
6257
+ if (newValue !== null && newValue !== "false") {
6258
+ this.chit.setAttribute("checkerboard", "");
6259
+ } else {
6260
+ this.chit.removeAttribute("checkerboard");
6261
+ }
6262
+ }
6263
+ }
5999
6264
  }
6000
6265
  }
6001
6266
  customElements.define("fig-image", FigImage);
@@ -6714,7 +6979,9 @@ class FigEasingCurve extends HTMLElement {
6714
6979
  this.#startBezierDrag(e, 2),
6715
6980
  );
6716
6981
 
6717
- const bezierSurface = this.querySelector(".fig-easing-curve-svg-container");
6982
+ const bezierSurface = this.querySelector(
6983
+ ".fig-easing-curve-svg-container",
6984
+ );
6718
6985
  if (bezierSurface) {
6719
6986
  bezierSurface.addEventListener("pointerdown", (e) => {
6720
6987
  // Handles keep their own direct drag behavior.
@@ -6732,7 +6999,9 @@ class FigEasingCurve extends HTMLElement {
6732
6999
  this.#startSpringDrag(e, "duration");
6733
7000
  });
6734
7001
 
6735
- const springSurface = this.querySelector(".fig-easing-curve-svg-container");
7002
+ const springSurface = this.querySelector(
7003
+ ".fig-easing-curve-svg-container",
7004
+ );
6736
7005
  if (springSurface) {
6737
7006
  springSurface.addEventListener("pointerdown", (e) => {
6738
7007
  // Bounce handle keeps its own drag mode/cursor.
@@ -6902,14 +7171,27 @@ class Fig3DRotate extends HTMLElement {
6902
7171
  #fieldInputs = {};
6903
7172
 
6904
7173
  static get observedAttributes() {
6905
- return ["value", "precision", "aspect-ratio", "fields", "perspective", "perspective-origin", "transform-origin", "selected", "drag"];
7174
+ return [
7175
+ "value",
7176
+ "precision",
7177
+ "aspect-ratio",
7178
+ "fields",
7179
+ "perspective",
7180
+ "perspective-origin",
7181
+ "transform-origin",
7182
+ "selected",
7183
+ "drag",
7184
+ ];
6906
7185
  }
6907
7186
 
6908
7187
  connectedCallback() {
6909
7188
  this.#precision = parseInt(this.getAttribute("precision") || "1");
6910
7189
  this.#syncAspectRatioVar(this.getAttribute("aspect-ratio"));
6911
7190
  this.#syncPerspectiveVar(this.getAttribute("perspective"));
6912
- this.#syncCSSVar("--perspective-origin", this.getAttribute("perspective-origin"));
7191
+ this.#syncCSSVar(
7192
+ "--perspective-origin",
7193
+ this.getAttribute("perspective-origin"),
7194
+ );
6913
7195
  this.#syncTransformOrigin(this.getAttribute("transform-origin"));
6914
7196
  this.#parseFields(this.getAttribute("fields"));
6915
7197
  const val = this.getAttribute("value");
@@ -6958,7 +7240,10 @@ class Fig3DRotate extends HTMLElement {
6958
7240
  }
6959
7241
  const parts = value.trim().split(/\s+/);
6960
7242
  if (parts.length === 2) {
6961
- this.style.setProperty("--transform-origin", `${parts[0]} ${parts[1]} -50cqi`);
7243
+ this.style.setProperty(
7244
+ "--transform-origin",
7245
+ `${parts[0]} ${parts[1]} -50cqi`,
7246
+ );
6962
7247
  } else {
6963
7248
  this.style.setProperty("--transform-origin", value.trim());
6964
7249
  }
@@ -6974,7 +7259,10 @@ class Fig3DRotate extends HTMLElement {
6974
7259
  const faces = this.#cube.querySelectorAll(".fig-3d-rotate-face");
6975
7260
  const name = value ? value.trim().toLowerCase() : "";
6976
7261
  for (const face of faces) {
6977
- face.classList.toggle("selected", name !== "" && face.classList.contains(name));
7262
+ face.classList.toggle(
7263
+ "selected",
7264
+ name !== "" && face.classList.contains(name),
7265
+ );
6978
7266
  }
6979
7267
  }
6980
7268
 
@@ -7062,7 +7350,11 @@ class Fig3DRotate extends HTMLElement {
7062
7350
 
7063
7351
  #render() {
7064
7352
  const axisLabels = { rotateX: "X", rotateY: "Y", rotateZ: "Z" };
7065
- const axisValues = { rotateX: this.#rx, rotateY: this.#ry, rotateZ: this.#rz };
7353
+ const axisValues = {
7354
+ rotateX: this.#rx,
7355
+ rotateY: this.#ry,
7356
+ rotateZ: this.#rz,
7357
+ };
7066
7358
  const fieldsHTML = this.#fields
7067
7359
  .map(
7068
7360
  (axis) =>
@@ -7115,17 +7407,21 @@ class Fig3DRotate extends HTMLElement {
7115
7407
  }
7116
7408
 
7117
7409
  #syncFieldInputs() {
7118
- const axisValues = { rotateX: this.#rx, rotateY: this.#ry, rotateZ: this.#rz };
7410
+ const axisValues = {
7411
+ rotateX: this.#rx,
7412
+ rotateY: this.#ry,
7413
+ rotateZ: this.#rz,
7414
+ };
7119
7415
  for (const axis of this.#fields) {
7120
7416
  const input = this.#fieldInputs[axis];
7121
- if (input) input.setAttribute("value", axisValues[axis].toFixed(this.#precision));
7417
+ if (input)
7418
+ input.setAttribute("value", axisValues[axis].toFixed(this.#precision));
7122
7419
  }
7123
7420
  }
7124
7421
 
7125
7422
  #updateCube() {
7126
7423
  if (!this.#cube) return;
7127
- this.#cube.style.transform =
7128
- `rotateX(${this.#rx}deg) rotateY(${this.#ry}deg) rotateZ(${this.#rz}deg)`;
7424
+ this.#cube.style.transform = `rotateX(${this.#rx}deg) rotateY(${this.#ry}deg) rotateZ(${this.#rz}deg)`;
7129
7425
  }
7130
7426
 
7131
7427
  #emit(type) {
@@ -7339,11 +7635,7 @@ class FigOriginGrid extends HTMLElement {
7339
7635
  }
7340
7636
 
7341
7637
  #parseValue(value) {
7342
- const parts = value
7343
- .trim()
7344
- .replace(/,/g, " ")
7345
- .split(/\s+/)
7346
- .filter(Boolean);
7638
+ const parts = value.trim().replace(/,/g, " ").split(/\s+/).filter(Boolean);
7347
7639
  if (parts.length < 1) return;
7348
7640
 
7349
7641
  if (parts.length === 1) {
@@ -7439,7 +7731,8 @@ class FigOriginGrid extends HTMLElement {
7439
7731
  #syncHandlePosition() {
7440
7732
  if (!this.#handle) return;
7441
7733
  // Constrain draggable visual bounds to the 3x3 dot centers.
7442
- const toVisual = (value) => 16.6667 + (this.#clampPercentage(value) / 100) * 66.6667;
7734
+ const toVisual = (value) =>
7735
+ 16.6667 + (this.#clampPercentage(value) / 100) * 66.6667;
7443
7736
  this.#handle.style.left = `${toVisual(this.#x)}%`;
7444
7737
  this.#handle.style.top = `${toVisual(this.#y)}%`;
7445
7738
  }
@@ -7553,10 +7846,18 @@ class FigOriginGrid extends HTMLElement {
7553
7846
  }
7554
7847
 
7555
7848
  #detachHandleDragListeners() {
7556
- if (!this.#grid || !this.#boundHandlePointerMove || !this.#boundHandlePointerEnd) return;
7849
+ if (
7850
+ !this.#grid ||
7851
+ !this.#boundHandlePointerMove ||
7852
+ !this.#boundHandlePointerEnd
7853
+ )
7854
+ return;
7557
7855
  this.#grid.removeEventListener("pointermove", this.#boundHandlePointerMove);
7558
7856
  this.#grid.removeEventListener("pointerup", this.#boundHandlePointerEnd);
7559
- this.#grid.removeEventListener("pointercancel", this.#boundHandlePointerEnd);
7857
+ this.#grid.removeEventListener(
7858
+ "pointercancel",
7859
+ this.#boundHandlePointerEnd,
7860
+ );
7560
7861
  this.#grid.removeEventListener(
7561
7862
  "lostpointercapture",
7562
7863
  this.#boundHandlePointerEnd,
@@ -7577,7 +7878,8 @@ class FigOriginGrid extends HTMLElement {
7577
7878
  this.#grid.setPointerCapture(e.pointerId);
7578
7879
 
7579
7880
  this.#boundHandlePointerMove = (moveEvent) => {
7580
- if (!this.#isDragging || moveEvent.pointerId !== this.#activePointerId) return;
7881
+ if (!this.#isDragging || moveEvent.pointerId !== this.#activePointerId)
7882
+ return;
7581
7883
  const dx = moveEvent.clientX - startClientX;
7582
7884
  const dy = moveEvent.clientY - startClientY;
7583
7885
  const distance = Math.hypot(dx, dy);
@@ -7595,7 +7897,8 @@ class FigOriginGrid extends HTMLElement {
7595
7897
  };
7596
7898
 
7597
7899
  this.#boundHandlePointerEnd = (endEvent) => {
7598
- if (!this.#isDragging || endEvent.pointerId !== this.#activePointerId) return;
7900
+ if (!this.#isDragging || endEvent.pointerId !== this.#activePointerId)
7901
+ return;
7599
7902
  this.#isDragging = false;
7600
7903
  this.#activePointerId = null;
7601
7904
  this.#grid.classList.remove("is-dragging");
@@ -7612,7 +7915,10 @@ class FigOriginGrid extends HTMLElement {
7612
7915
  this.#grid.addEventListener("pointermove", this.#boundHandlePointerMove);
7613
7916
  this.#grid.addEventListener("pointerup", this.#boundHandlePointerEnd);
7614
7917
  this.#grid.addEventListener("pointercancel", this.#boundHandlePointerEnd);
7615
- this.#grid.addEventListener("lostpointercapture", this.#boundHandlePointerEnd);
7918
+ this.#grid.addEventListener(
7919
+ "lostpointercapture",
7920
+ this.#boundHandlePointerEnd,
7921
+ );
7616
7922
  }
7617
7923
 
7618
7924
  #setupEvents() {
@@ -8064,7 +8370,8 @@ class FigInputJoystick extends HTMLElement {
8064
8370
  const isPercent = token.includes("%");
8065
8371
  const numeric = Number.parseFloat(token.replace(/%/g, "").trim());
8066
8372
  if (!Number.isFinite(numeric)) return 0.5;
8067
- const decimal = isPercent || Math.abs(numeric) > 1 ? numeric / 100 : numeric;
8373
+ const decimal =
8374
+ isPercent || Math.abs(numeric) > 1 ? numeric / 100 : numeric;
8068
8375
  return Math.max(0, Math.min(1, decimal));
8069
8376
  };
8070
8377
  const x = parseAxis(parts[0]);
@@ -10480,7 +10787,9 @@ class FigChooser extends HTMLElement {
10480
10787
  }
10481
10788
 
10482
10789
  get #overflowMode() {
10483
- return this.getAttribute("overflow") === "scrollbar" ? "scrollbar" : "buttons";
10790
+ return this.getAttribute("overflow") === "scrollbar"
10791
+ ? "scrollbar"
10792
+ : "buttons";
10484
10793
  }
10485
10794
 
10486
10795
  get #dragEnabled() {
@@ -10730,12 +11039,14 @@ class FigChooser extends HTMLElement {
10730
11039
 
10731
11040
  if (isHorizontal) {
10732
11041
  const atStart = this.scrollLeft <= threshold;
10733
- const atEnd = this.scrollLeft + this.clientWidth >= this.scrollWidth - threshold;
11042
+ const atEnd =
11043
+ this.scrollLeft + this.clientWidth >= this.scrollWidth - threshold;
10734
11044
  this.classList.toggle("overflow-start", !atStart);
10735
11045
  this.classList.toggle("overflow-end", !atEnd);
10736
11046
  } else {
10737
11047
  const atStart = this.scrollTop <= threshold;
10738
- const atEnd = this.scrollTop + this.clientHeight >= this.scrollHeight - threshold;
11048
+ const atEnd =
11049
+ this.scrollTop + this.clientHeight >= this.scrollHeight - threshold;
10739
11050
  this.classList.toggle("overflow-start", !atStart);
10740
11051
  this.classList.toggle("overflow-end", !atEnd);
10741
11052
  }
@@ -10800,7 +11111,9 @@ class FigChooser extends HTMLElement {
10800
11111
  this.style.cursor = "";
10801
11112
  this.style.userSelect = "";
10802
11113
  if (e.pointerId !== undefined) {
10803
- try { this.releasePointerCapture(e.pointerId); } catch {}
11114
+ try {
11115
+ this.releasePointerCapture(e.pointerId);
11116
+ } catch {}
10804
11117
  }
10805
11118
  if (wasDrag) {
10806
11119
  e.preventDefault();
@@ -10853,7 +11166,11 @@ class FigChooser extends HTMLElement {
10853
11166
  this.removeEventListener("pointerdown", this.#dragState.onPointerDown);
10854
11167
  window.removeEventListener("pointermove", this.#dragState.onPointerMove);
10855
11168
  window.removeEventListener("pointerup", this.#dragState.onPointerUp);
10856
- this.removeEventListener("pointerup", this.#dragState.onPointerUpCapture, true);
11169
+ this.removeEventListener(
11170
+ "pointerup",
11171
+ this.#dragState.onPointerUpCapture,
11172
+ true,
11173
+ );
10857
11174
  this.removeEventListener("click", this.#dragState.onClick, true);
10858
11175
  this.style.cursor = "";
10859
11176
  this.style.userSelect = "";
@@ -10904,8 +11221,7 @@ class FigChooser extends HTMLElement {
10904
11221
  }
10905
11222
 
10906
11223
  #scrollByPage(direction) {
10907
- const isHorizontal =
10908
- this.getAttribute("layout") === "horizontal";
11224
+ const isHorizontal = this.getAttribute("layout") === "horizontal";
10909
11225
  const pageSize = isHorizontal ? this.clientWidth : this.clientHeight;
10910
11226
  const scrollAmount = pageSize * 0.8 * direction;
10911
11227
 
@@ -10925,12 +11241,14 @@ class FigChooser extends HTMLElement {
10925
11241
  const options = { behavior: "smooth" };
10926
11242
 
10927
11243
  if (overflowY) {
10928
- const target = el.offsetTop - this.clientHeight / 2 + el.offsetHeight / 2;
11244
+ const target =
11245
+ el.offsetTop - this.clientHeight / 2 + el.offsetHeight / 2;
10929
11246
  options.top = target;
10930
11247
  }
10931
11248
 
10932
11249
  if (overflowX) {
10933
- const target = el.offsetLeft - this.clientWidth / 2 + el.offsetWidth / 2;
11250
+ const target =
11251
+ el.offsetLeft - this.clientWidth / 2 + el.offsetWidth / 2;
10934
11252
  options.left = target;
10935
11253
  }
10936
11254