@rogieking/figui3 6.40.1 → 7.1.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/fig-editor.js CHANGED
@@ -247,7 +247,7 @@ class FigFillPicker extends HTMLElement {
247
247
  this.#swatch.setAttribute("background", "#D9D9D9");
248
248
  this.appendChild(this.#swatch);
249
249
  this.#trigger = this.#swatch;
250
- } else if (child.matches("fig-swatch")) {
250
+ } else if (child.matches("fig-swatch, fig-chit")) {
251
251
  // Scenario 2: Has swatch - use and populate it
252
252
  this.#swatch = child;
253
253
  this.#trigger = child;
package/fig.js CHANGED
@@ -12678,6 +12678,11 @@ class FigSwatch extends HTMLElement {
12678
12678
  }
12679
12679
  figDefineElement("fig-swatch", FigSwatch);
12680
12680
 
12681
+ // Backwards-compatible alias. Custom element constructors cannot be registered
12682
+ // under multiple tag names, so the legacy tag uses an otherwise empty subclass.
12683
+ class FigChit extends FigSwatch {}
12684
+ figDefineElement("fig-chit", FigChit);
12685
+
12681
12686
  /* Media */
12682
12687
  const FIG_IMAGE_FORWARDED_EVENTS = ["load", "error"];
12683
12688
  const FIG_VIDEO_FORWARDED_EVENTS = [
@@ -13611,22 +13616,19 @@ class FigVideo extends FigMedia {
13611
13616
  figDefineElement("fig-video", FigVideo);
13612
13617
 
13613
13618
  /**
13614
- * <fig-card> — Media card with optional link, selection chrome, and truncated label.
13619
+ * <fig-card> — Media card with selection chrome and truncated labels.
13615
13620
  *
13616
13621
  * Composes a generated `fig-image` when `src` is set. Attribute-driven labels
13617
13622
  * render in a generated `fig-footer`; authored footers remain untouched.
13618
13623
  * Selection is attribute-only (`selected`); apps own toggle/group logic.
13619
- * When `href` is set (and not disabled), content wraps in a real `<a>`.
13620
13624
  *
13621
13625
  * @attr {string} src - Image URL forwarded to generated fig-image
13622
13626
  * @attr {string} alt - Alt text forwarded to generated fig-image
13623
13627
  * @attr {string} label - Card title text (preferred over `text`)
13624
13628
  * @attr {string} text - Alias for `label`
13625
13629
  * @attr {string} sublabel - Secondary one-line text under the label
13626
- * @attr {string} href - Makes the card a link
13627
- * @attr {string} target - Forwarded to the `<a>`
13628
13630
  * @attr {boolean} selected - Selected chrome (CSS only)
13629
- * @attr {boolean} disabled - Dim + non-interactive; no `<a>` when disabled
13631
+ * @attr {boolean} disabled - Dim + non-interactive
13630
13632
  * @attr {boolean} full - Stretch to available width (CSS; default width is already 100%)
13631
13633
  * @attr {string} size - Set to `large` for spacer-2 card padding and spacer-1 label spacing
13632
13634
  * @attr {string} aspect-ratio - Forwarded to fig-image (default `1/1`)
@@ -13641,8 +13643,6 @@ class FigCard extends HTMLElement {
13641
13643
  "label",
13642
13644
  "text",
13643
13645
  "sublabel",
13644
- "href",
13645
- "target",
13646
13646
  "selected",
13647
13647
  "disabled",
13648
13648
  "full",
@@ -13653,7 +13653,6 @@ class FigCard extends HTMLElement {
13653
13653
  ];
13654
13654
  }
13655
13655
 
13656
- #linkEl = null;
13657
13656
  #footerEl = null;
13658
13657
  #imageEl = null;
13659
13658
  #labelEl = null;
@@ -13701,11 +13700,6 @@ class FigCard extends HTMLElement {
13701
13700
  return this.getAttribute("sublabel") ?? "";
13702
13701
  }
13703
13702
 
13704
- #wantsLink() {
13705
- const href = this.getAttribute("href");
13706
- return Boolean(href) && !figBooleanAttribute(this, "disabled");
13707
- }
13708
-
13709
13703
  #lineClamp() {
13710
13704
  const raw = this.getAttribute("label-line-clamp");
13711
13705
  return raw === "2" ? "2" : "1";
@@ -13717,65 +13711,48 @@ class FigCard extends HTMLElement {
13717
13711
 
13718
13712
  #findAuthoredMedia() {
13719
13713
  return this.querySelector(
13720
- ":scope > fig-image:not([data-generated]), :scope > fig-media:not([data-generated]), :scope > fig-preview:not([data-generated]), :scope > .fig-card-link > fig-image:not([data-generated]), :scope > .fig-card-link > fig-media:not([data-generated]), :scope > .fig-card-link > fig-preview:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-image:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-media:not([data-generated]), :scope > .fig-card-link > .fig-card-media > fig-preview:not([data-generated])",
13714
+ ":scope > fig-image:not([data-generated]), :scope > fig-media:not([data-generated]), :scope > fig-preview:not([data-generated])",
13721
13715
  );
13722
13716
  }
13723
13717
 
13718
+ #unwrapLegacyStructure() {
13719
+ this.querySelectorAll(":scope > .fig-card-link").forEach((wrapper) => {
13720
+ wrapper.querySelectorAll(":scope > .fig-card-media").forEach((mediaWrap) => {
13721
+ while (mediaWrap.firstChild) {
13722
+ wrapper.insertBefore(mediaWrap.firstChild, mediaWrap);
13723
+ }
13724
+ mediaWrap.remove();
13725
+ });
13726
+ while (wrapper.firstChild) {
13727
+ this.insertBefore(wrapper.firstChild, wrapper);
13728
+ }
13729
+ wrapper.remove();
13730
+ });
13731
+ }
13732
+
13724
13733
  #ensureStructure() {
13734
+ this.#unwrapLegacyStructure();
13725
13735
  if (!this.#usesGeneratedMedia()) {
13726
13736
  this.#ensureManualStructure();
13727
13737
  return;
13728
13738
  }
13729
13739
 
13730
- this.querySelectorAll(":scope > fig-footer[data-generated]").forEach(
13731
- (element) => element.remove(),
13732
- );
13733
- if (this.#footerEl?.parentElement === this) this.#footerEl = null;
13734
-
13735
13740
  const authored = this.#findAuthoredMedia();
13736
13741
  this.#usesAuthoredMedia = Boolean(authored);
13737
13742
 
13738
- const wantAnchor = this.#wantsLink();
13739
- const tag = wantAnchor ? "a" : "div";
13740
-
13741
- if (!this.#linkEl || this.#linkEl.tagName.toLowerCase() !== tag) {
13742
- const next = document.createElement(tag);
13743
- next.className = "fig-card-link";
13744
- if (this.#linkEl) {
13745
- while (this.#linkEl.firstChild) {
13746
- next.appendChild(this.#linkEl.firstChild);
13747
- }
13748
- this.#linkEl.replaceWith(next);
13749
- } else {
13750
- this.appendChild(next);
13751
- }
13752
- this.#linkEl = next;
13753
- }
13754
-
13755
- const legacyMediaWrap = this.#linkEl.querySelector(
13756
- ":scope > .fig-card-media",
13757
- );
13758
- if (legacyMediaWrap) {
13759
- while (legacyMediaWrap.firstChild) {
13760
- this.#linkEl.insertBefore(legacyMediaWrap.firstChild, legacyMediaWrap);
13761
- }
13762
- legacyMediaWrap.remove();
13763
- }
13764
-
13765
- if (authored && authored.parentElement !== this.#linkEl) {
13766
- this.#linkEl
13743
+ if (authored) {
13744
+ this
13767
13745
  .querySelectorAll(":scope > [data-generated]:not(fig-footer)")
13768
13746
  .forEach((el) => el.remove());
13769
- this.#linkEl.prepend(authored);
13770
13747
  this.#imageEl = null;
13771
13748
  }
13772
13749
 
13773
13750
  if (!this.#usesAuthoredMedia) {
13774
13751
  this.#imageEl =
13775
- this.#linkEl.querySelector(":scope > fig-image[data-generated]") ||
13752
+ this.querySelector(":scope > fig-image[data-generated]") ||
13776
13753
  null;
13777
13754
  if (!this.#imageEl) {
13778
- this.#linkEl
13755
+ this
13779
13756
  .querySelectorAll(
13780
13757
  ":scope > fig-image, :scope > fig-media, :scope > fig-preview",
13781
13758
  )
@@ -13786,7 +13763,7 @@ class FigCard extends HTMLElement {
13786
13763
  this.#imageEl.setAttribute("data-generated", "");
13787
13764
  this.#imageEl.setAttribute("full", "");
13788
13765
  this.#imageEl.setAttribute("size", "auto");
13789
- this.#linkEl.prepend(this.#imageEl);
13766
+ this.prepend(this.#imageEl);
13790
13767
  }
13791
13768
  }
13792
13769
 
@@ -13794,9 +13771,7 @@ class FigCard extends HTMLElement {
13794
13771
  this.#labelText() || this.#sublabelText(),
13795
13772
  );
13796
13773
  if (!hasGeneratedLabels) {
13797
- this.#linkEl
13798
- .querySelector(":scope > fig-footer[data-generated]")
13799
- ?.remove();
13774
+ this.querySelector(":scope > fig-footer[data-generated]")?.remove();
13800
13775
  this.#footerEl = null;
13801
13776
  this.#labelEl = null;
13802
13777
  this.#sublabelEl = null;
@@ -13804,13 +13779,13 @@ class FigCard extends HTMLElement {
13804
13779
  }
13805
13780
 
13806
13781
  if (!this.#footerEl || !this.#footerEl.isConnected) {
13807
- this.#footerEl = this.#linkEl.querySelector(
13782
+ this.#footerEl = this.querySelector(
13808
13783
  ":scope > fig-footer[data-generated]",
13809
13784
  );
13810
13785
  if (!this.#footerEl) {
13811
13786
  this.#footerEl = document.createElement("fig-footer");
13812
13787
  this.#footerEl.setAttribute("data-generated", "");
13813
- this.#linkEl.appendChild(this.#footerEl);
13788
+ this.appendChild(this.#footerEl);
13814
13789
  }
13815
13790
  }
13816
13791
 
@@ -13819,14 +13794,9 @@ class FigCard extends HTMLElement {
13819
13794
  ":scope > .fig-card-label[data-generated]",
13820
13795
  );
13821
13796
  if (!this.#labelEl) {
13822
- this.#labelEl =
13823
- this.#linkEl.querySelector(":scope > .fig-card-label[data-generated]") ||
13824
- null;
13825
- if (!this.#labelEl) {
13826
- this.#labelEl = document.createElement("label");
13827
- this.#labelEl.className = "fig-card-label";
13828
- this.#labelEl.setAttribute("data-generated", "");
13829
- }
13797
+ this.#labelEl = document.createElement("label");
13798
+ this.#labelEl.className = "fig-card-label";
13799
+ this.#labelEl.setAttribute("data-generated", "");
13830
13800
  this.#footerEl.appendChild(this.#labelEl);
13831
13801
  }
13832
13802
  }
@@ -13836,36 +13806,20 @@ class FigCard extends HTMLElement {
13836
13806
  ":scope > .fig-card-sublabel[data-generated]",
13837
13807
  );
13838
13808
  if (!this.#sublabelEl) {
13839
- this.#sublabelEl =
13840
- this.#linkEl.querySelector(
13841
- ":scope > .fig-card-sublabel[data-generated]",
13842
- ) || null;
13843
- if (!this.#sublabelEl) {
13844
- this.#sublabelEl = document.createElement("label");
13845
- this.#sublabelEl.className = "fig-card-sublabel";
13846
- this.#sublabelEl.setAttribute("data-generated", "");
13847
- }
13809
+ this.#sublabelEl = document.createElement("label");
13810
+ this.#sublabelEl.className = "fig-card-sublabel";
13811
+ this.#sublabelEl.setAttribute("data-generated", "");
13848
13812
  this.#labelEl.after(this.#sublabelEl);
13849
13813
  }
13850
13814
  }
13851
13815
  }
13852
13816
 
13853
13817
  #ensureManualStructure() {
13854
- const staleLink =
13855
- this.#linkEl || this.querySelector(":scope > .fig-card-link");
13856
- if (staleLink) {
13857
- staleLink
13858
- .querySelectorAll(
13859
- "fig-image:not([data-generated]), fig-media:not([data-generated]), fig-preview:not([data-generated]), fig-footer:not([data-generated])",
13860
- )
13861
- .forEach((element) => this.insertBefore(element, staleLink));
13862
- staleLink.remove();
13863
- }
13818
+ this.#unwrapLegacyStructure();
13864
13819
  this.querySelectorAll(":scope > [data-generated]").forEach((element) =>
13865
13820
  element.remove(),
13866
13821
  );
13867
13822
 
13868
- this.#linkEl = null;
13869
13823
  this.#imageEl = null;
13870
13824
  this.#usesAuthoredMedia = true;
13871
13825
 
@@ -13898,50 +13852,20 @@ class FigCard extends HTMLElement {
13898
13852
  this.#syncManual();
13899
13853
  return;
13900
13854
  }
13901
- if (!this.#linkEl) return;
13902
13855
 
13903
13856
  const disabled = figBooleanAttribute(this, "disabled");
13904
- const selected = figBooleanAttribute(this, "selected");
13905
13857
  const label = this.#labelText();
13906
13858
  const sublabel = this.#sublabelText();
13907
13859
  const clamp = this.#lineClamp();
13908
13860
 
13909
13861
  this.style.setProperty("--fig-card-label-line-clamp", clamp);
13910
13862
 
13911
- if (this.#wantsLink()) {
13912
- const href = this.getAttribute("href") || "";
13913
- const target = this.getAttribute("target");
13914
- this.#linkEl.setAttribute("href", href);
13915
- if (target) {
13916
- this.#linkEl.setAttribute("target", target);
13917
- if (target === "_blank") {
13918
- this.#linkEl.setAttribute("rel", "noopener noreferrer");
13919
- } else {
13920
- this.#linkEl.removeAttribute("rel");
13921
- }
13922
- } else {
13923
- this.#linkEl.removeAttribute("target");
13924
- this.#linkEl.removeAttribute("rel");
13925
- }
13926
- if (selected) {
13927
- this.#linkEl.setAttribute("aria-current", "true");
13928
- } else {
13929
- this.#linkEl.removeAttribute("aria-current");
13930
- }
13931
- this.removeAttribute("role");
13932
- this.removeAttribute("aria-label");
13863
+ this.setAttribute("role", "group");
13864
+ const ariaLabel = [label, sublabel].filter(Boolean).join(", ");
13865
+ if (ariaLabel) {
13866
+ this.setAttribute("aria-label", ariaLabel);
13933
13867
  } else {
13934
- this.#linkEl.removeAttribute("href");
13935
- this.#linkEl.removeAttribute("target");
13936
- this.#linkEl.removeAttribute("rel");
13937
- this.#linkEl.removeAttribute("aria-current");
13938
- this.setAttribute("role", "group");
13939
- const ariaLabel = [label, sublabel].filter(Boolean).join(", ");
13940
- if (ariaLabel) {
13941
- this.setAttribute("aria-label", ariaLabel);
13942
- } else {
13943
- this.removeAttribute("aria-label");
13944
- }
13868
+ this.removeAttribute("aria-label");
13945
13869
  }
13946
13870
 
13947
13871
  if (disabled) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "6.40.1",
3
+ "version": "7.1.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",