@rogieking/figui3 6.22.0 → 6.23.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/components.css +28 -23
- package/dist/components.css +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +22 -22
- package/dist/propskit.css +1 -1
- package/fig.js +136 -84
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -105,9 +105,12 @@ function createFigOverflowButtons({
|
|
|
105
105
|
startClass = "",
|
|
106
106
|
endClass = "",
|
|
107
107
|
chevronClass = "",
|
|
108
|
+
startLabel = "Scroll back",
|
|
109
|
+
endLabel = "Scroll forward",
|
|
108
110
|
} = {}) {
|
|
109
111
|
const makeButton = (direction, onPointerDown) => {
|
|
110
112
|
const button = document.createElement("button");
|
|
113
|
+
button.type = "button";
|
|
111
114
|
button.className = [
|
|
112
115
|
"fig-overflow",
|
|
113
116
|
`fig-overflow-${direction}`,
|
|
@@ -118,7 +121,10 @@ function createFigOverflowButtons({
|
|
|
118
121
|
button.dataset.figOverflow = direction;
|
|
119
122
|
if (owner) button.setAttribute(`data-fig-${owner}-nav`, direction);
|
|
120
123
|
button.setAttribute("tabindex", "-1");
|
|
121
|
-
button.setAttribute(
|
|
124
|
+
button.setAttribute(
|
|
125
|
+
"aria-label",
|
|
126
|
+
direction === "start" ? startLabel : endLabel,
|
|
127
|
+
);
|
|
122
128
|
button.appendChild(
|
|
123
129
|
createFigIcon("chevron", {
|
|
124
130
|
size: "small",
|
|
@@ -164,6 +170,48 @@ function figScrollOverflowPage(scrollEl, axis = "x", direction = 1) {
|
|
|
164
170
|
});
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
function figScrollElementToCenter(
|
|
174
|
+
scrollEl,
|
|
175
|
+
element,
|
|
176
|
+
axis = "y",
|
|
177
|
+
behavior = "auto",
|
|
178
|
+
) {
|
|
179
|
+
if (!scrollEl || !element || !scrollEl.contains(element)) return;
|
|
180
|
+
requestAnimationFrame(() => {
|
|
181
|
+
if (!scrollEl.isConnected || !element.isConnected) return;
|
|
182
|
+
const isHorizontal = axis === "x";
|
|
183
|
+
const scrollSize = isHorizontal
|
|
184
|
+
? scrollEl.scrollWidth
|
|
185
|
+
: scrollEl.scrollHeight;
|
|
186
|
+
const clientSize = isHorizontal
|
|
187
|
+
? scrollEl.clientWidth
|
|
188
|
+
: scrollEl.clientHeight;
|
|
189
|
+
if (scrollSize <= clientSize + 1) {
|
|
190
|
+
figSyncOverflowState(scrollEl, scrollEl, axis);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const elementRect = element.getBoundingClientRect();
|
|
194
|
+
const hostRect = scrollEl.getBoundingClientRect();
|
|
195
|
+
const currentScroll = isHorizontal
|
|
196
|
+
? scrollEl.scrollLeft
|
|
197
|
+
: scrollEl.scrollTop;
|
|
198
|
+
const elementStart =
|
|
199
|
+
(isHorizontal ? elementRect.left - hostRect.left : elementRect.top - hostRect.top) +
|
|
200
|
+
currentScroll;
|
|
201
|
+
const elementSize = isHorizontal ? elementRect.width : elementRect.height;
|
|
202
|
+
const maxScroll = scrollSize - clientSize;
|
|
203
|
+
const nextScroll = Math.max(
|
|
204
|
+
0,
|
|
205
|
+
Math.min(elementStart + elementSize / 2 - clientSize / 2, maxScroll),
|
|
206
|
+
);
|
|
207
|
+
scrollEl.scrollTo({
|
|
208
|
+
[isHorizontal ? "left" : "top"]: nextScroll,
|
|
209
|
+
behavior,
|
|
210
|
+
});
|
|
211
|
+
figSyncOverflowState(scrollEl, scrollEl, axis);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
167
215
|
function hasFigFillPicker() {
|
|
168
216
|
return typeof customElements !== "undefined" && !!customElements.get("fig-fill-picker");
|
|
169
217
|
}
|
|
@@ -5220,57 +5268,6 @@ class FigSelectOption extends HTMLElement {
|
|
|
5220
5268
|
}
|
|
5221
5269
|
figDefineElement("fig-select-option", FigSelectOption);
|
|
5222
5270
|
|
|
5223
|
-
function figSelectSyncOverflowState(host, scrollEl, threshold = 2) {
|
|
5224
|
-
if (!host || !scrollEl) return false;
|
|
5225
|
-
const scrollable = scrollEl.scrollHeight - scrollEl.clientHeight > threshold;
|
|
5226
|
-
const atStart = !scrollable || scrollEl.scrollTop <= threshold;
|
|
5227
|
-
const atEnd =
|
|
5228
|
-
!scrollable ||
|
|
5229
|
-
scrollEl.scrollTop + scrollEl.clientHeight >=
|
|
5230
|
-
scrollEl.scrollHeight - threshold;
|
|
5231
|
-
host.classList.toggle("overflow-start", !atStart);
|
|
5232
|
-
host.classList.toggle("overflow-end", !atEnd);
|
|
5233
|
-
return scrollable;
|
|
5234
|
-
}
|
|
5235
|
-
|
|
5236
|
-
function figSelectScrollOverflowPage(scrollEl, direction = 1) {
|
|
5237
|
-
if (!scrollEl) return;
|
|
5238
|
-
scrollEl.scrollBy({
|
|
5239
|
-
top: scrollEl.clientHeight * 0.8 * direction,
|
|
5240
|
-
behavior: "smooth",
|
|
5241
|
-
});
|
|
5242
|
-
}
|
|
5243
|
-
|
|
5244
|
-
function figSelectCreateOverflowButtons({ onStart, onEnd } = {}) {
|
|
5245
|
-
const makeButton = (direction, onClick) => {
|
|
5246
|
-
const button = document.createElement("button");
|
|
5247
|
-
button.type = "button";
|
|
5248
|
-
button.className = `fig-overflow fig-overflow-${direction}`;
|
|
5249
|
-
button.dataset.figOverflow = direction;
|
|
5250
|
-
button.setAttribute("data-fig-select-nav", direction);
|
|
5251
|
-
button.setAttribute("tabindex", "-1");
|
|
5252
|
-
button.setAttribute(
|
|
5253
|
-
"aria-label",
|
|
5254
|
-
direction === "start" ? "Scroll up" : "Scroll down",
|
|
5255
|
-
);
|
|
5256
|
-
const icon = document.createElement("fig-icon");
|
|
5257
|
-
icon.setAttribute("name", "chevron");
|
|
5258
|
-
icon.setAttribute("size", "small");
|
|
5259
|
-
icon.className = "fig-overflow-chevron";
|
|
5260
|
-
button.appendChild(icon);
|
|
5261
|
-
button.addEventListener("click", (event) => {
|
|
5262
|
-
event.preventDefault();
|
|
5263
|
-
event.stopPropagation();
|
|
5264
|
-
onClick?.(event);
|
|
5265
|
-
});
|
|
5266
|
-
return button;
|
|
5267
|
-
};
|
|
5268
|
-
return {
|
|
5269
|
-
start: makeButton("start", onStart),
|
|
5270
|
-
end: makeButton("end", onEnd),
|
|
5271
|
-
};
|
|
5272
|
-
}
|
|
5273
|
-
|
|
5274
5271
|
/** Light-DOM panel wrapper projected into fig-select's popup; owns overflow buttons. */
|
|
5275
5272
|
class FigSelectOptions extends HTMLElement {
|
|
5276
5273
|
#navStart = null;
|
|
@@ -5297,31 +5294,11 @@ class FigSelectOptions extends HTMLElement {
|
|
|
5297
5294
|
}
|
|
5298
5295
|
|
|
5299
5296
|
syncOverflow() {
|
|
5300
|
-
return
|
|
5297
|
+
return figSyncOverflowState(this, this, "y");
|
|
5301
5298
|
}
|
|
5302
5299
|
|
|
5303
5300
|
scrollToOption(option, behavior = "auto") {
|
|
5304
|
-
|
|
5305
|
-
requestAnimationFrame(() => {
|
|
5306
|
-
if (!option.isConnected) return;
|
|
5307
|
-
if (this.scrollHeight <= this.clientHeight + 1) {
|
|
5308
|
-
this.syncOverflow();
|
|
5309
|
-
return;
|
|
5310
|
-
}
|
|
5311
|
-
const optionRect = option.getBoundingClientRect();
|
|
5312
|
-
const hostRect = this.getBoundingClientRect();
|
|
5313
|
-
const optionTop = optionRect.top - hostRect.top + this.scrollTop;
|
|
5314
|
-
const maxScroll = this.scrollHeight - this.clientHeight;
|
|
5315
|
-
const top = Math.max(
|
|
5316
|
-
0,
|
|
5317
|
-
Math.min(
|
|
5318
|
-
optionTop + optionRect.height / 2 - this.clientHeight / 2,
|
|
5319
|
-
maxScroll,
|
|
5320
|
-
),
|
|
5321
|
-
);
|
|
5322
|
-
this.scrollTo({ top, behavior });
|
|
5323
|
-
this.syncOverflow();
|
|
5324
|
-
});
|
|
5301
|
+
figScrollElementToCenter(this, option, "y", behavior);
|
|
5325
5302
|
}
|
|
5326
5303
|
|
|
5327
5304
|
#unwrapLegacyChooser() {
|
|
@@ -5343,9 +5320,12 @@ class FigSelectOptions extends HTMLElement {
|
|
|
5343
5320
|
return;
|
|
5344
5321
|
}
|
|
5345
5322
|
this.#removeNavButtons();
|
|
5346
|
-
const buttons =
|
|
5347
|
-
|
|
5348
|
-
|
|
5323
|
+
const buttons = createFigOverflowButtons({
|
|
5324
|
+
owner: "select",
|
|
5325
|
+
startLabel: "Scroll up",
|
|
5326
|
+
endLabel: "Scroll down",
|
|
5327
|
+
onStart: () => figScrollOverflowPage(this, "y", -1),
|
|
5328
|
+
onEnd: () => figScrollOverflowPage(this, "y", 1),
|
|
5349
5329
|
});
|
|
5350
5330
|
this.#navStart = buttons.start;
|
|
5351
5331
|
this.#navEnd = buttons.end;
|
|
@@ -19366,14 +19346,19 @@ figDefineElement("fig-menu-separator", FigMenuSeparator);
|
|
|
19366
19346
|
|
|
19367
19347
|
class FigMenu extends HTMLElement {
|
|
19368
19348
|
#popup = null;
|
|
19349
|
+
#panel = null;
|
|
19369
19350
|
#trigger = null;
|
|
19370
19351
|
#virtualAnchor = null;
|
|
19371
19352
|
#observer = null;
|
|
19353
|
+
#resizeObserver = null;
|
|
19354
|
+
#navStart = null;
|
|
19355
|
+
#navEnd = null;
|
|
19372
19356
|
#boundTriggerClick;
|
|
19373
19357
|
#boundTriggerContextMenu;
|
|
19374
19358
|
#boundPopupClick;
|
|
19375
19359
|
#boundMenuKeydown;
|
|
19376
19360
|
#boundPopupClose;
|
|
19361
|
+
#boundSyncOverflow = this.#syncOverflow.bind(this);
|
|
19377
19362
|
#focusedIndex = -1;
|
|
19378
19363
|
|
|
19379
19364
|
static get observedAttributes() {
|
|
@@ -19415,6 +19400,7 @@ class FigMenu extends HTMLElement {
|
|
|
19415
19400
|
this.#createPopup();
|
|
19416
19401
|
this.#moveItemsToPopup();
|
|
19417
19402
|
this.#setupListeners();
|
|
19403
|
+
this.#setupOverflow();
|
|
19418
19404
|
this.#setupObserver();
|
|
19419
19405
|
this.#syncDisabled();
|
|
19420
19406
|
|
|
@@ -19425,6 +19411,7 @@ class FigMenu extends HTMLElement {
|
|
|
19425
19411
|
|
|
19426
19412
|
disconnectedCallback() {
|
|
19427
19413
|
this.#teardownListeners();
|
|
19414
|
+
this.#teardownOverflow();
|
|
19428
19415
|
document.removeEventListener("keydown", this.#boundMenuKeydown, true);
|
|
19429
19416
|
if (this.#observer) {
|
|
19430
19417
|
this.#observer.disconnect();
|
|
@@ -19433,13 +19420,14 @@ class FigMenu extends HTMLElement {
|
|
|
19433
19420
|
if (this.#popup) {
|
|
19434
19421
|
this.#popup.removeEventListener("close", this.#boundPopupClose);
|
|
19435
19422
|
const items = Array.from(
|
|
19436
|
-
this.#
|
|
19423
|
+
this.#panel?.querySelectorAll(
|
|
19437
19424
|
":scope > fig-menu-item, :scope > fig-menu-separator",
|
|
19438
|
-
),
|
|
19425
|
+
) ?? [],
|
|
19439
19426
|
);
|
|
19440
19427
|
for (const item of items) this.insertBefore(item, this.#popup);
|
|
19441
19428
|
this.#popup.remove();
|
|
19442
19429
|
this.#popup = null;
|
|
19430
|
+
this.#panel = null;
|
|
19443
19431
|
}
|
|
19444
19432
|
}
|
|
19445
19433
|
|
|
@@ -19487,6 +19475,7 @@ class FigMenu extends HTMLElement {
|
|
|
19487
19475
|
#createPopup() {
|
|
19488
19476
|
this.#popup = document.createElement("dialog", { is: "fig-popup" });
|
|
19489
19477
|
this.#popup.setAttribute("is", "fig-popup");
|
|
19478
|
+
this.#popup.classList.add("fig-menu-popup");
|
|
19490
19479
|
this.#popup.setAttribute("theme", "menu");
|
|
19491
19480
|
this.#popup.setAttribute("role", "menu");
|
|
19492
19481
|
this.#popup.setAttribute("id", this.#popup.getAttribute("id") || figUniqueId());
|
|
@@ -19504,16 +19493,21 @@ class FigMenu extends HTMLElement {
|
|
|
19504
19493
|
this.#popup.anchor = this.#trigger;
|
|
19505
19494
|
}
|
|
19506
19495
|
|
|
19496
|
+
this.#panel = document.createElement("div");
|
|
19497
|
+
this.#panel.className = "fig-menu-options";
|
|
19498
|
+
this.#panel.setAttribute("role", "presentation");
|
|
19499
|
+
this.#popup.appendChild(this.#panel);
|
|
19507
19500
|
this.#popup.addEventListener("close", this.#boundPopupClose);
|
|
19508
19501
|
this.appendChild(this.#popup);
|
|
19509
19502
|
}
|
|
19510
19503
|
|
|
19511
19504
|
#moveItemsToPopup() {
|
|
19505
|
+
if (!this.#panel) return;
|
|
19512
19506
|
const items = Array.from(this.querySelectorAll(
|
|
19513
19507
|
":scope > fig-menu-item, :scope > fig-menu-separator"
|
|
19514
19508
|
));
|
|
19515
19509
|
for (const item of items) {
|
|
19516
|
-
this.#
|
|
19510
|
+
this.#panel.appendChild(item);
|
|
19517
19511
|
}
|
|
19518
19512
|
}
|
|
19519
19513
|
|
|
@@ -19553,7 +19547,7 @@ class FigMenu extends HTMLElement {
|
|
|
19553
19547
|
(node.tagName === "FIG-MENU-ITEM" || node.tagName === "FIG-MENU-SEPARATOR") &&
|
|
19554
19548
|
node.parentElement === this
|
|
19555
19549
|
) {
|
|
19556
|
-
this.#
|
|
19550
|
+
this.#panel?.appendChild(node);
|
|
19557
19551
|
} else if (!this.#trigger && node.parentElement === this) {
|
|
19558
19552
|
this.#detectTrigger();
|
|
19559
19553
|
if (this.#trigger) {
|
|
@@ -19568,13 +19562,68 @@ class FigMenu extends HTMLElement {
|
|
|
19568
19562
|
}
|
|
19569
19563
|
}
|
|
19570
19564
|
}
|
|
19565
|
+
this.#syncOverflow();
|
|
19571
19566
|
});
|
|
19572
19567
|
this.#observer.observe(this, { childList: true });
|
|
19573
19568
|
}
|
|
19574
19569
|
|
|
19570
|
+
#setupOverflow() {
|
|
19571
|
+
if (!this.#panel) return;
|
|
19572
|
+
this.#ensureNavButtons();
|
|
19573
|
+
this.#panel.addEventListener("scroll", this.#boundSyncOverflow, {
|
|
19574
|
+
passive: true,
|
|
19575
|
+
});
|
|
19576
|
+
this.#resizeObserver?.disconnect();
|
|
19577
|
+
this.#resizeObserver = new ResizeObserver(() => this.#syncOverflow());
|
|
19578
|
+
this.#resizeObserver.observe(this.#panel);
|
|
19579
|
+
requestAnimationFrame(() => this.#syncOverflow());
|
|
19580
|
+
}
|
|
19581
|
+
|
|
19582
|
+
#teardownOverflow() {
|
|
19583
|
+
this.#panel?.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
19584
|
+
this.#resizeObserver?.disconnect();
|
|
19585
|
+
this.#resizeObserver = null;
|
|
19586
|
+
this.#removeNavButtons();
|
|
19587
|
+
}
|
|
19588
|
+
|
|
19589
|
+
#syncOverflow() {
|
|
19590
|
+
return figSyncOverflowState(this.#panel, this.#panel, "y");
|
|
19591
|
+
}
|
|
19592
|
+
|
|
19593
|
+
#ensureNavButtons() {
|
|
19594
|
+
if (
|
|
19595
|
+
this.#navStart &&
|
|
19596
|
+
this.#navEnd &&
|
|
19597
|
+
this.#panel?.contains(this.#navStart) &&
|
|
19598
|
+
this.#panel?.contains(this.#navEnd)
|
|
19599
|
+
) {
|
|
19600
|
+
return;
|
|
19601
|
+
}
|
|
19602
|
+
this.#removeNavButtons();
|
|
19603
|
+
const buttons = createFigOverflowButtons({
|
|
19604
|
+
owner: "menu",
|
|
19605
|
+
startLabel: "Scroll up",
|
|
19606
|
+
endLabel: "Scroll down",
|
|
19607
|
+
onStart: () => figScrollOverflowPage(this.#panel, "y", -1),
|
|
19608
|
+
onEnd: () => figScrollOverflowPage(this.#panel, "y", 1),
|
|
19609
|
+
});
|
|
19610
|
+
this.#navStart = buttons.start;
|
|
19611
|
+
this.#navEnd = buttons.end;
|
|
19612
|
+
this.#panel.prepend(this.#navStart);
|
|
19613
|
+
this.#panel.append(this.#navEnd);
|
|
19614
|
+
}
|
|
19615
|
+
|
|
19616
|
+
#removeNavButtons() {
|
|
19617
|
+
this.#navStart?.remove();
|
|
19618
|
+
this.#navEnd?.remove();
|
|
19619
|
+
this.#navStart = null;
|
|
19620
|
+
this.#navEnd = null;
|
|
19621
|
+
this.#panel?.classList.remove("overflow-start", "overflow-end");
|
|
19622
|
+
}
|
|
19623
|
+
|
|
19575
19624
|
#getItems() {
|
|
19576
|
-
if (!this.#
|
|
19577
|
-
return Array.from(this.#
|
|
19625
|
+
if (!this.#panel) return [];
|
|
19626
|
+
return Array.from(this.#panel.querySelectorAll("fig-menu-item")).filter(
|
|
19578
19627
|
(item) =>
|
|
19579
19628
|
!item.hasAttribute("disabled") || item.getAttribute("disabled") === "false",
|
|
19580
19629
|
);
|
|
@@ -19598,7 +19647,9 @@ class FigMenu extends HTMLElement {
|
|
|
19598
19647
|
if (!items.length) return;
|
|
19599
19648
|
const wrapped = (index + items.length) % items.length;
|
|
19600
19649
|
this.#focusedIndex = wrapped;
|
|
19601
|
-
items[wrapped]
|
|
19650
|
+
const item = items[wrapped];
|
|
19651
|
+
item.focus();
|
|
19652
|
+
figScrollElementToCenter(this.#panel, item, "y");
|
|
19602
19653
|
}
|
|
19603
19654
|
|
|
19604
19655
|
#syncDisabled() {
|
|
@@ -19809,6 +19860,7 @@ class FigMenu extends HTMLElement {
|
|
|
19809
19860
|
}
|
|
19810
19861
|
this.#focusedIndex = -1;
|
|
19811
19862
|
requestAnimationFrame(() => {
|
|
19863
|
+
this.#syncOverflow();
|
|
19812
19864
|
if (!this.#trigger?.matches?.(":focus-visible")) return;
|
|
19813
19865
|
this.#focusItemAt(0);
|
|
19814
19866
|
});
|
package/package.json
CHANGED