@rogieking/figui3 6.29.1 → 6.30.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/dist/fig-editor.css +1 -1
- package/dist/fig-lab.css +1 -1
- package/dist/fig-lab.js +3 -3
- package/fig-lab.css +81 -0
- package/fig-lab.js +199 -0
- package/package.json +1 -1
package/fig-lab.css
CHANGED
|
@@ -63,6 +63,12 @@ fig-ai-prompt {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
& > fig-attachments {
|
|
67
|
+
box-sizing: border-box;
|
|
68
|
+
width: auto;
|
|
69
|
+
margin: var(--spacer-2) var(--spacer-3) 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
66
72
|
& > fig-footer {
|
|
67
73
|
justify-content: space-between;
|
|
68
74
|
border: none;
|
|
@@ -70,6 +76,81 @@ fig-ai-prompt {
|
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
78
|
|
|
79
|
+
fig-attachments {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-wrap: wrap;
|
|
82
|
+
align-items: flex-start;
|
|
83
|
+
gap: var(--spacer-2);
|
|
84
|
+
width: 100%;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fig-attachment {
|
|
89
|
+
--fig-attachment-size: 2.5rem;
|
|
90
|
+
|
|
91
|
+
position: relative;
|
|
92
|
+
display: block;
|
|
93
|
+
flex: 0 0 var(--fig-attachment-size);
|
|
94
|
+
width: var(--fig-attachment-size);
|
|
95
|
+
min-width: 0;
|
|
96
|
+
|
|
97
|
+
fig-image {
|
|
98
|
+
display: flex;
|
|
99
|
+
width: 100%;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.fig-attachment-fallback {
|
|
103
|
+
position: absolute;
|
|
104
|
+
inset: 0;
|
|
105
|
+
display: none;
|
|
106
|
+
place-items: center;
|
|
107
|
+
border-radius: var(--radius-medium);
|
|
108
|
+
background: var(--figma-color-bg-secondary);
|
|
109
|
+
box-shadow: inset 0 0 0 1px var(--figma-color-bordertranslucent);
|
|
110
|
+
color: var(--figma-color-text-secondary);
|
|
111
|
+
font-size: var(--body-medium-fontSize);
|
|
112
|
+
font-weight: 500;
|
|
113
|
+
line-height: 1;
|
|
114
|
+
pointer-events: none;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&[data-fallback] {
|
|
118
|
+
.fig-attachment-fallback {
|
|
119
|
+
display: grid;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.fig-attachment-remove {
|
|
124
|
+
--spacer-2: 0px;
|
|
125
|
+
--spacer-4: 1.25rem;
|
|
126
|
+
|
|
127
|
+
position: absolute;
|
|
128
|
+
z-index: 3;
|
|
129
|
+
top: var(--spacer-1);
|
|
130
|
+
right: var(--spacer-1);
|
|
131
|
+
width: 1.25rem;
|
|
132
|
+
min-width: 1.25rem;
|
|
133
|
+
height: 1.25rem;
|
|
134
|
+
padding: 0;
|
|
135
|
+
opacity: 0;
|
|
136
|
+
pointer-events: none;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&:hover .fig-attachment-remove,
|
|
140
|
+
&:focus-within .fig-attachment-remove {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
pointer-events: auto;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
&[disabled]:not([disabled="false"]) {
|
|
146
|
+
opacity: 0.5;
|
|
147
|
+
|
|
148
|
+
.fig-attachment-remove {
|
|
149
|
+
pointer-events: none;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
73
154
|
fig-chat-message {
|
|
74
155
|
display: block;
|
|
75
156
|
box-sizing: border-box;
|
package/fig-lab.js
CHANGED
|
@@ -193,6 +193,205 @@ figLabDefineElement("fig-ai-prompt", FigAiPrompt);
|
|
|
193
193
|
class FigChatMessage extends HTMLElement {}
|
|
194
194
|
figLabDefineElement("fig-chat-message", FigChatMessage);
|
|
195
195
|
|
|
196
|
+
/**
|
|
197
|
+
* A removable image attachment for compact prompt surfaces.
|
|
198
|
+
*
|
|
199
|
+
* @attr {string} src - Image preview URL.
|
|
200
|
+
* @attr {string} name - Attachment name used for the tooltip and image alt text.
|
|
201
|
+
* @attr {string} value - Optional application-owned attachment identifier.
|
|
202
|
+
* @attr {boolean|string} removable - "false" hides the remove button.
|
|
203
|
+
* @attr {boolean|string} disabled - Disables removal.
|
|
204
|
+
* @fires remove - Cancelable request for the owning application to remove the attachment.
|
|
205
|
+
*/
|
|
206
|
+
class FigAttachment extends HTMLElement {
|
|
207
|
+
#tooltip = null;
|
|
208
|
+
#image = null;
|
|
209
|
+
#fallback = null;
|
|
210
|
+
#removeTooltip = null;
|
|
211
|
+
#removeButton = null;
|
|
212
|
+
#mediaImage = null;
|
|
213
|
+
#boundHandleRemove = this.#handleRemove.bind(this);
|
|
214
|
+
#boundHandleImageLoad = this.#handleImageLoad.bind(this);
|
|
215
|
+
#boundHandleImageError = this.#handleImageError.bind(this);
|
|
216
|
+
|
|
217
|
+
static get observedAttributes() {
|
|
218
|
+
return ["src", "name", "value", "removable", "disabled"];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
connectedCallback() {
|
|
222
|
+
if (!this.#image) this.#initialize();
|
|
223
|
+
this.#removeButton.removeEventListener("click", this.#boundHandleRemove);
|
|
224
|
+
this.#removeButton.addEventListener("click", this.#boundHandleRemove);
|
|
225
|
+
this.#bindMediaImage();
|
|
226
|
+
this.#sync();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
disconnectedCallback() {
|
|
230
|
+
this.#removeButton?.removeEventListener("click", this.#boundHandleRemove);
|
|
231
|
+
this.#unbindMediaImage();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
235
|
+
if (oldValue === newValue || !this.#image) return;
|
|
236
|
+
this.#sync();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
#initialize() {
|
|
240
|
+
this.#tooltip = document.createElement("fig-tooltip");
|
|
241
|
+
this.#tooltip.className = "fig-attachment-tooltip";
|
|
242
|
+
|
|
243
|
+
this.#image = document.createElement("fig-image");
|
|
244
|
+
this.#image.setAttribute("aspect-ratio", "1/1");
|
|
245
|
+
this.#image.setAttribute("fit", "cover");
|
|
246
|
+
this.#image.setAttribute("full", "");
|
|
247
|
+
|
|
248
|
+
this.#fallback = document.createElement("span");
|
|
249
|
+
this.#fallback.className = "fig-attachment-fallback";
|
|
250
|
+
this.#fallback.setAttribute("aria-hidden", "true");
|
|
251
|
+
|
|
252
|
+
this.#removeTooltip = document.createElement("fig-tooltip");
|
|
253
|
+
this.#removeTooltip.className = "fig-attachment-remove-tooltip";
|
|
254
|
+
this.#removeTooltip.setAttribute("text", "Remove attachment");
|
|
255
|
+
|
|
256
|
+
this.#removeButton = document.createElement("fig-button");
|
|
257
|
+
this.#removeButton.className = "fig-attachment-remove";
|
|
258
|
+
this.#removeButton.setAttribute("variant", "overlay");
|
|
259
|
+
this.#removeButton.setAttribute("size", "small");
|
|
260
|
+
this.#removeButton.setAttribute("icon", "");
|
|
261
|
+
const closeIcon = document.createElement("fig-icon");
|
|
262
|
+
closeIcon.setAttribute("name", "close");
|
|
263
|
+
closeIcon.setAttribute("size", "small");
|
|
264
|
+
this.#removeButton.append(closeIcon);
|
|
265
|
+
this.#removeTooltip.append(this.#removeButton);
|
|
266
|
+
|
|
267
|
+
this.#image.append(this.#fallback);
|
|
268
|
+
this.#tooltip.append(this.#image);
|
|
269
|
+
this.replaceChildren(this.#tooltip, this.#removeTooltip);
|
|
270
|
+
|
|
271
|
+
this.#removeButton.addEventListener("click", this.#boundHandleRemove);
|
|
272
|
+
this.#bindMediaImage();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
#bindMediaImage() {
|
|
276
|
+
const mediaImage = this.#image?.mediaEl;
|
|
277
|
+
if (!mediaImage || mediaImage === this.#mediaImage) return;
|
|
278
|
+
this.#unbindMediaImage();
|
|
279
|
+
this.#mediaImage = mediaImage;
|
|
280
|
+
this.#mediaImage.addEventListener("load", this.#boundHandleImageLoad);
|
|
281
|
+
this.#mediaImage.addEventListener("error", this.#boundHandleImageError);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
#unbindMediaImage() {
|
|
285
|
+
if (!this.#mediaImage) return;
|
|
286
|
+
this.#mediaImage.removeEventListener("load", this.#boundHandleImageLoad);
|
|
287
|
+
this.#mediaImage.removeEventListener("error", this.#boundHandleImageError);
|
|
288
|
+
this.#mediaImage = null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
#sync() {
|
|
292
|
+
const src = this.getAttribute("src") || "";
|
|
293
|
+
const name = this.getAttribute("name") || "Attachment";
|
|
294
|
+
const removable =
|
|
295
|
+
!this.hasAttribute("removable") || figLabBooleanAttribute(this, "removable");
|
|
296
|
+
const disabled = figLabBooleanAttribute(this, "disabled");
|
|
297
|
+
|
|
298
|
+
this.#tooltip.setAttribute("text", name);
|
|
299
|
+
this.#image.setAttribute("alt", name);
|
|
300
|
+
if (src) {
|
|
301
|
+
this.#image.setAttribute("src", src);
|
|
302
|
+
} else {
|
|
303
|
+
this.#image.removeAttribute("src");
|
|
304
|
+
}
|
|
305
|
+
this.#bindMediaImage();
|
|
306
|
+
|
|
307
|
+
this.#fallback.textContent = this.#fallbackLabel(name);
|
|
308
|
+
this.#removeButton.hidden = !removable;
|
|
309
|
+
this.#removeButton.toggleAttribute("disabled", disabled);
|
|
310
|
+
this.#removeButton.setAttribute("aria-label", `Remove ${name}`);
|
|
311
|
+
this.toggleAttribute("data-fallback", !src);
|
|
312
|
+
if (disabled) {
|
|
313
|
+
this.setAttribute("aria-disabled", "true");
|
|
314
|
+
} else {
|
|
315
|
+
this.removeAttribute("aria-disabled");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
#fallbackLabel(name) {
|
|
320
|
+
const baseName = name.split(/[\\/]/).pop() || "";
|
|
321
|
+
const dotIndex = baseName.lastIndexOf(".");
|
|
322
|
+
const extension =
|
|
323
|
+
dotIndex > 0 && dotIndex < baseName.length - 1
|
|
324
|
+
? baseName.slice(dotIndex + 1).toUpperCase()
|
|
325
|
+
: "FILE";
|
|
326
|
+
return extension.slice(0, 4);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#handleImageLoad() {
|
|
330
|
+
if (this.getAttribute("src")) this.removeAttribute("data-fallback");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#handleImageError() {
|
|
334
|
+
this.setAttribute("data-fallback", "");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#handleRemove(event) {
|
|
338
|
+
event.stopPropagation();
|
|
339
|
+
if (
|
|
340
|
+
figLabBooleanAttribute(this, "disabled") ||
|
|
341
|
+
(this.hasAttribute("removable") &&
|
|
342
|
+
!figLabBooleanAttribute(this, "removable"))
|
|
343
|
+
) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
this.dispatchEvent(
|
|
347
|
+
new CustomEvent("remove", {
|
|
348
|
+
bubbles: true,
|
|
349
|
+
cancelable: true,
|
|
350
|
+
composed: true,
|
|
351
|
+
detail: {
|
|
352
|
+
value: this.getAttribute("value"),
|
|
353
|
+
name: this.getAttribute("name") || "",
|
|
354
|
+
src: this.getAttribute("src") || "",
|
|
355
|
+
attachment: this,
|
|
356
|
+
},
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
figLabDefineElement("fig-attachment", FigAttachment);
|
|
362
|
+
|
|
363
|
+
/* Wrapping presentation container for one or more attachments. */
|
|
364
|
+
class FigAttachments extends HTMLElement {
|
|
365
|
+
#observer = null;
|
|
366
|
+
|
|
367
|
+
connectedCallback() {
|
|
368
|
+
if (!this.hasAttribute("role")) {
|
|
369
|
+
this.setAttribute("role", "list");
|
|
370
|
+
this.setAttribute("data-generated-role", "");
|
|
371
|
+
}
|
|
372
|
+
this.#syncItems();
|
|
373
|
+
if (!this.#observer) {
|
|
374
|
+
this.#observer = new MutationObserver(() => this.#syncItems());
|
|
375
|
+
this.#observer.observe(this, { childList: true });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
disconnectedCallback() {
|
|
380
|
+
this.#observer?.disconnect();
|
|
381
|
+
this.#observer = null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
#syncItems() {
|
|
385
|
+
for (const attachment of this.querySelectorAll(":scope > fig-attachment")) {
|
|
386
|
+
if (!attachment.hasAttribute("role")) {
|
|
387
|
+
attachment.setAttribute("role", "listitem");
|
|
388
|
+
attachment.setAttribute("data-generated-role", "");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
figLabDefineElement("fig-attachments", FigAttachments);
|
|
394
|
+
|
|
196
395
|
/* Field + Switch wrapper */
|
|
197
396
|
class PropskitSwitch extends HTMLElement {
|
|
198
397
|
#field = null;
|
package/package.json
CHANGED