@rogieking/figui3 10.0.1 → 10.0.3
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/.cursor/skills/figui3/components.md +8 -3
- package/CHANGELOG.md +28 -0
- package/README.md +12 -1
- package/components.css +74 -35
- package/dist/components.css +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +5 -5
- package/fig.js +69 -30
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -13126,6 +13126,7 @@ const FIG_MEDIA_FORWARDED_EVENTS = [
|
|
|
13126
13126
|
* @attr {string} src - Media source URL
|
|
13127
13127
|
* @attr {string} type - "image" (default) or "video" (for fig-media)
|
|
13128
13128
|
* @attr {string} alt - Alt text for the generated image (default "")
|
|
13129
|
+
* @attr {boolean} loaded - Read-only reflected state when an image has loaded successfully
|
|
13129
13130
|
* @attr {boolean} upload - Show upload overlay (generates fig-input-file)
|
|
13130
13131
|
* @attr {boolean} loading-indicator - Set to `false` to disable the generated image loading spinner
|
|
13131
13132
|
* @attr {string} label - Upload button label (default "Upload")
|
|
@@ -13344,6 +13345,7 @@ class FigMedia extends HTMLElement {
|
|
|
13344
13345
|
}
|
|
13345
13346
|
|
|
13346
13347
|
#syncImageLoadingState() {
|
|
13348
|
+
this.#syncImageLoadedState();
|
|
13347
13349
|
const src = this.#currentMediaSrc();
|
|
13348
13350
|
if (!src || !this.#usesDefaultImageLoadingIndicator()) {
|
|
13349
13351
|
this.#finishImageLoading();
|
|
@@ -13383,6 +13385,18 @@ class FigMedia extends HTMLElement {
|
|
|
13383
13385
|
}, 150);
|
|
13384
13386
|
}
|
|
13385
13387
|
|
|
13388
|
+
#syncImageLoadedState() {
|
|
13389
|
+
const image = this.#mediaEl?.tagName === "IMG" ? this.#mediaEl : null;
|
|
13390
|
+
const src =
|
|
13391
|
+
image?.currentSrc ||
|
|
13392
|
+
image?.getAttribute("src") ||
|
|
13393
|
+
this.#currentMediaSrc();
|
|
13394
|
+
this.toggleAttribute(
|
|
13395
|
+
"loaded",
|
|
13396
|
+
Boolean(src && image?.complete && image.naturalWidth > 0),
|
|
13397
|
+
);
|
|
13398
|
+
}
|
|
13399
|
+
|
|
13386
13400
|
#finishImageLoading() {
|
|
13387
13401
|
if (this.#loadingTimer !== null) {
|
|
13388
13402
|
clearTimeout(this.#loadingTimer);
|
|
@@ -13553,6 +13567,7 @@ class FigMedia extends HTMLElement {
|
|
|
13553
13567
|
if (!this.#mediaEl.hasAttribute("data-generated")) return;
|
|
13554
13568
|
const src = this.#currentMediaSrc();
|
|
13555
13569
|
if (this.#mediaEl.getAttribute("src") !== src) {
|
|
13570
|
+
this.removeAttribute("loaded");
|
|
13556
13571
|
if (src) {
|
|
13557
13572
|
this.#mediaEl.setAttribute("src", src);
|
|
13558
13573
|
} else {
|
|
@@ -13880,6 +13895,7 @@ class FigMedia extends HTMLElement {
|
|
|
13880
13895
|
media.tagName === "IMG" &&
|
|
13881
13896
|
(event.type === "load" || event.type === "error")
|
|
13882
13897
|
) {
|
|
13898
|
+
this.toggleAttribute("loaded", event.type === "load");
|
|
13883
13899
|
this.#finishImageLoading();
|
|
13884
13900
|
}
|
|
13885
13901
|
const detail = {
|
|
@@ -14057,9 +14073,11 @@ figDefineElement("fig-video", FigVideo);
|
|
|
14057
14073
|
* @attr {boolean} disabled - Dim + non-interactive
|
|
14058
14074
|
* @attr {boolean} full - Stretch to available width (CSS; default width is already 100%)
|
|
14059
14075
|
* @attr {string} size - Set to `large` for spacer-2 card padding and spacer-1 label spacing
|
|
14076
|
+
* @attr {string} direction - `vertical` (default) or `horizontal` list layout
|
|
14060
14077
|
* @attr {string} aspect-ratio - Forwarded to fig-image (default `1/1`)
|
|
14061
14078
|
* @attr {string} fit - Forwarded to fig-image (default `contain`)
|
|
14062
14079
|
* @attr {string} label-line-clamp - `1` (default) or `2`
|
|
14080
|
+
* @attr {string} label-overflow - `ellipsis` (default) or horizontal `fade`
|
|
14063
14081
|
*/
|
|
14064
14082
|
class FigCard extends HTMLElement {
|
|
14065
14083
|
static get observedAttributes() {
|
|
@@ -14073,9 +14091,11 @@ class FigCard extends HTMLElement {
|
|
|
14073
14091
|
"disabled",
|
|
14074
14092
|
"full",
|
|
14075
14093
|
"size",
|
|
14094
|
+
"direction",
|
|
14076
14095
|
"aspect-ratio",
|
|
14077
14096
|
"fit",
|
|
14078
14097
|
"label-line-clamp",
|
|
14098
|
+
"label-overflow",
|
|
14079
14099
|
];
|
|
14080
14100
|
}
|
|
14081
14101
|
|
|
@@ -14273,6 +14293,16 @@ class FigCard extends HTMLElement {
|
|
|
14273
14293
|
this.appendChild(this.#footerEl);
|
|
14274
14294
|
}
|
|
14275
14295
|
|
|
14296
|
+
#syncLabelOverflow() {
|
|
14297
|
+
const fade = this.getAttribute("label-overflow") === "fade";
|
|
14298
|
+
this.querySelectorAll(
|
|
14299
|
+
":scope > fig-footer > :is(.fig-card-label, .fig-card-sublabel)",
|
|
14300
|
+
).forEach((element) => {
|
|
14301
|
+
element.classList.toggle("fig-overflow-fade", fade);
|
|
14302
|
+
element.classList.toggle("fig-overflow-fade-horizontal", fade);
|
|
14303
|
+
});
|
|
14304
|
+
}
|
|
14305
|
+
|
|
14276
14306
|
#sync() {
|
|
14277
14307
|
if (!this.#usesGeneratedMedia()) {
|
|
14278
14308
|
this.#syncManual();
|
|
@@ -14329,6 +14359,7 @@ class FigCard extends HTMLElement {
|
|
|
14329
14359
|
if (this.#footerEl) {
|
|
14330
14360
|
this.#footerEl.hidden = !label && !sublabel;
|
|
14331
14361
|
}
|
|
14362
|
+
this.#syncLabelOverflow();
|
|
14332
14363
|
}
|
|
14333
14364
|
|
|
14334
14365
|
#syncManual() {
|
|
@@ -14352,6 +14383,7 @@ class FigCard extends HTMLElement {
|
|
|
14352
14383
|
this.#labelEl.hidden = !label;
|
|
14353
14384
|
}
|
|
14354
14385
|
if (this.#sublabelEl) this.#sublabelEl.textContent = sublabel;
|
|
14386
|
+
this.#syncLabelOverflow();
|
|
14355
14387
|
}
|
|
14356
14388
|
}
|
|
14357
14389
|
figDefineElement("fig-card", FigCard);
|
|
@@ -19772,6 +19804,8 @@ figDefineElement("fig-color-tip", FigColorTip);
|
|
|
19772
19804
|
* @attr {boolean} disabled - Whether this choice is disabled
|
|
19773
19805
|
*/
|
|
19774
19806
|
class FigChoice extends HTMLElement {
|
|
19807
|
+
#childObserver = null;
|
|
19808
|
+
|
|
19775
19809
|
static get observedAttributes() {
|
|
19776
19810
|
return ["selected", "disabled"];
|
|
19777
19811
|
}
|
|
@@ -19781,21 +19815,34 @@ class FigChoice extends HTMLElement {
|
|
|
19781
19815
|
if (!this.hasAttribute("tabindex")) {
|
|
19782
19816
|
this.setAttribute("tabindex", "0");
|
|
19783
19817
|
}
|
|
19784
|
-
this
|
|
19785
|
-
"aria-selected",
|
|
19786
|
-
figBooleanAttribute(this, "selected") ? "true" : "false",
|
|
19787
|
-
);
|
|
19818
|
+
this.#syncSelectedState();
|
|
19788
19819
|
if (figBooleanAttribute(this, "disabled")) {
|
|
19789
19820
|
this.setAttribute("aria-disabled", "true");
|
|
19790
19821
|
}
|
|
19791
19822
|
}
|
|
19792
19823
|
|
|
19824
|
+
disconnectedCallback() {
|
|
19825
|
+
this.#childObserver?.disconnect();
|
|
19826
|
+
}
|
|
19827
|
+
|
|
19828
|
+
#syncSelectedState() {
|
|
19829
|
+
const selected = figBooleanAttribute(this, "selected");
|
|
19830
|
+
this.setAttribute("aria-selected", selected ? "true" : "false");
|
|
19831
|
+
for (const child of this.children) {
|
|
19832
|
+
child.toggleAttribute("selected", selected);
|
|
19833
|
+
}
|
|
19834
|
+
this.#childObserver?.disconnect();
|
|
19835
|
+
if (selected && this.isConnected) {
|
|
19836
|
+
this.#childObserver ??= new MutationObserver(() =>
|
|
19837
|
+
this.#syncSelectedState(),
|
|
19838
|
+
);
|
|
19839
|
+
this.#childObserver.observe(this, { childList: true });
|
|
19840
|
+
}
|
|
19841
|
+
}
|
|
19842
|
+
|
|
19793
19843
|
attributeChangedCallback(name) {
|
|
19794
19844
|
if (name === "selected") {
|
|
19795
|
-
this
|
|
19796
|
-
"aria-selected",
|
|
19797
|
-
figBooleanAttribute(this, "selected") ? "true" : "false",
|
|
19798
|
-
);
|
|
19845
|
+
this.#syncSelectedState();
|
|
19799
19846
|
}
|
|
19800
19847
|
if (name === "disabled") {
|
|
19801
19848
|
const isDisabled =
|
|
@@ -19898,19 +19945,11 @@ class FigChooser extends HTMLElement {
|
|
|
19898
19945
|
const raw = this.getAttribute("columns");
|
|
19899
19946
|
const columns = raw === null ? NaN : Number(raw);
|
|
19900
19947
|
const validColumns = Number.isInteger(columns) && columns > 0;
|
|
19901
|
-
const effectiveColumns = validColumns ? columns : 2;
|
|
19902
19948
|
if (validColumns) {
|
|
19903
|
-
this.style.setProperty("--fig-chooser-grid-columns", String(
|
|
19949
|
+
this.style.setProperty("--fig-chooser-grid-columns", String(columns));
|
|
19904
19950
|
} else {
|
|
19905
19951
|
this.style.removeProperty("--fig-chooser-grid-columns");
|
|
19906
19952
|
}
|
|
19907
|
-
const isGrid = this.getAttribute("layout") === "grid";
|
|
19908
|
-
this.choices.forEach((choice, index) => {
|
|
19909
|
-
choice.toggleAttribute(
|
|
19910
|
-
"data-fig-chooser-first-row",
|
|
19911
|
-
isGrid && index < effectiveColumns,
|
|
19912
|
-
);
|
|
19913
|
-
});
|
|
19914
19953
|
}
|
|
19915
19954
|
|
|
19916
19955
|
get choices() {
|
|
@@ -20160,7 +20199,8 @@ class FigChooser extends HTMLElement {
|
|
|
20160
20199
|
!c.hasAttribute("disabled") || c.getAttribute("disabled") === "false",
|
|
20161
20200
|
);
|
|
20162
20201
|
if (!choices.length) return;
|
|
20163
|
-
const
|
|
20202
|
+
const focusedChoice = this.#findChoiceFromTarget(event.target);
|
|
20203
|
+
const currentIndex = choices.indexOf(focusedChoice);
|
|
20164
20204
|
let nextIndex = currentIndex;
|
|
20165
20205
|
|
|
20166
20206
|
const loop = this.hasAttribute("loop");
|
|
@@ -20195,12 +20235,9 @@ class FigChooser extends HTMLElement {
|
|
|
20195
20235
|
case "Enter":
|
|
20196
20236
|
case " ":
|
|
20197
20237
|
event.preventDefault();
|
|
20198
|
-
if (
|
|
20199
|
-
|
|
20200
|
-
|
|
20201
|
-
this.selectedChoice = focused;
|
|
20202
|
-
this.#emitEvents();
|
|
20203
|
-
}
|
|
20238
|
+
if (focusedChoice && focusedChoice !== this.#selectedChoice) {
|
|
20239
|
+
this.selectedChoice = focusedChoice;
|
|
20240
|
+
this.#emitEvents();
|
|
20204
20241
|
}
|
|
20205
20242
|
return;
|
|
20206
20243
|
default:
|
|
@@ -20208,9 +20245,9 @@ class FigChooser extends HTMLElement {
|
|
|
20208
20245
|
}
|
|
20209
20246
|
|
|
20210
20247
|
if (nextIndex !== currentIndex && choices[nextIndex]) {
|
|
20211
|
-
|
|
20212
|
-
|
|
20213
|
-
this.#
|
|
20248
|
+
const nextChoice = choices[nextIndex];
|
|
20249
|
+
nextChoice.focus({ preventScroll: true });
|
|
20250
|
+
if (this.autoScroll) this.#scrollToChoice(nextChoice);
|
|
20214
20251
|
}
|
|
20215
20252
|
}
|
|
20216
20253
|
|
|
@@ -20381,12 +20418,14 @@ class FigChooser extends HTMLElement {
|
|
|
20381
20418
|
}
|
|
20382
20419
|
|
|
20383
20420
|
#applyOverflowMode() {
|
|
20384
|
-
|
|
20421
|
+
const usesScrollbar = this.#overflowMode === "scrollbar";
|
|
20422
|
+
const isGrid = this.getAttribute("layout") === "grid";
|
|
20423
|
+
if (usesScrollbar || isGrid) {
|
|
20385
20424
|
this.#removeNavButtons();
|
|
20386
|
-
this.classList.
|
|
20425
|
+
this.classList.toggle("fig-overflow-fade", usesScrollbar);
|
|
20387
20426
|
this.classList.toggle(
|
|
20388
20427
|
"fig-overflow-fade-horizontal",
|
|
20389
|
-
this.getAttribute("layout") === "horizontal",
|
|
20428
|
+
usesScrollbar && this.getAttribute("layout") === "horizontal",
|
|
20390
20429
|
);
|
|
20391
20430
|
} else {
|
|
20392
20431
|
this.#createNavButtons();
|
package/package.json
CHANGED