@rogieking/figui3 6.4.7 → 6.5.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/README.md +1 -0
- package/components.css +161 -109
- package/dist/components.css +1 -1
- package/dist/fig.css +1 -1
- package/dist/fig.js +33 -33
- package/fig.js +375 -81
- package/package.json +1 -1
package/fig.js
CHANGED
|
@@ -27,6 +27,72 @@ function createFigIcon(name, options = {}) {
|
|
|
27
27
|
return icon;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function createFigOverflowButtons({
|
|
31
|
+
owner,
|
|
32
|
+
onStart,
|
|
33
|
+
onEnd,
|
|
34
|
+
startClass = "",
|
|
35
|
+
endClass = "",
|
|
36
|
+
chevronClass = "",
|
|
37
|
+
} = {}) {
|
|
38
|
+
const makeButton = (direction, onPointerDown) => {
|
|
39
|
+
const button = document.createElement("button");
|
|
40
|
+
button.className = [
|
|
41
|
+
"fig-overflow",
|
|
42
|
+
`fig-overflow-${direction}`,
|
|
43
|
+
direction === "start" ? startClass : endClass,
|
|
44
|
+
]
|
|
45
|
+
.filter(Boolean)
|
|
46
|
+
.join(" ");
|
|
47
|
+
button.dataset.figOverflow = direction;
|
|
48
|
+
if (owner) button.setAttribute(`data-fig-${owner}-nav`, direction);
|
|
49
|
+
button.setAttribute("tabindex", "-1");
|
|
50
|
+
button.setAttribute("aria-label", direction === "start" ? "Scroll back" : "Scroll forward");
|
|
51
|
+
button.appendChild(
|
|
52
|
+
createFigIcon("chevron", {
|
|
53
|
+
size: "small",
|
|
54
|
+
className: ["fig-overflow-chevron", chevronClass].filter(Boolean).join(" "),
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
button.addEventListener("pointerdown", (event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
event.stopPropagation();
|
|
60
|
+
onPointerDown?.(event);
|
|
61
|
+
});
|
|
62
|
+
return button;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
start: makeButton("start", onStart),
|
|
67
|
+
end: makeButton("end", onEnd),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function figSyncOverflowState(host, scrollEl, axis = "x", threshold = 2) {
|
|
72
|
+
if (!host || !scrollEl) return false;
|
|
73
|
+
const isHorizontal = axis === "x";
|
|
74
|
+
const scrollSize = isHorizontal ? scrollEl.scrollWidth : scrollEl.scrollHeight;
|
|
75
|
+
const clientSize = isHorizontal ? scrollEl.clientWidth : scrollEl.clientHeight;
|
|
76
|
+
const scrollPosition = isHorizontal ? scrollEl.scrollLeft : scrollEl.scrollTop;
|
|
77
|
+
const scrollable = scrollSize - clientSize > threshold;
|
|
78
|
+
const atStart = !scrollable || scrollPosition <= threshold;
|
|
79
|
+
const atEnd = !scrollable || scrollPosition + clientSize >= scrollSize - threshold;
|
|
80
|
+
host.classList.toggle("overflow-start", !atStart);
|
|
81
|
+
host.classList.toggle("overflow-end", !atEnd);
|
|
82
|
+
return scrollable;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function figScrollOverflowPage(scrollEl, axis = "x", direction = 1) {
|
|
86
|
+
if (!scrollEl) return;
|
|
87
|
+
const isHorizontal = axis === "x";
|
|
88
|
+
const pageSize = isHorizontal ? scrollEl.clientWidth : scrollEl.clientHeight;
|
|
89
|
+
const scrollAmount = pageSize * 0.8 * direction;
|
|
90
|
+
scrollEl.scrollBy({
|
|
91
|
+
[isHorizontal ? "left" : "top"]: scrollAmount,
|
|
92
|
+
behavior: "smooth",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
30
96
|
function hasFigFillPicker() {
|
|
31
97
|
return typeof customElements !== "undefined" && !!customElements.get("fig-fill-picker");
|
|
32
98
|
}
|
|
@@ -3117,6 +3183,14 @@ customElements.define("fig-tab", FigTab);
|
|
|
3117
3183
|
class FigTabs extends HTMLElement {
|
|
3118
3184
|
#boundHandleClick;
|
|
3119
3185
|
#boundHandleKeyDown;
|
|
3186
|
+
#boundSyncOverflow = this.#syncOverflow.bind(this);
|
|
3187
|
+
#mutationObserver = null;
|
|
3188
|
+
#resizeObserver = null;
|
|
3189
|
+
#scroller = null;
|
|
3190
|
+
#navStart = null;
|
|
3191
|
+
#navEnd = null;
|
|
3192
|
+
#isStructuring = false;
|
|
3193
|
+
|
|
3120
3194
|
constructor() {
|
|
3121
3195
|
super();
|
|
3122
3196
|
this.#boundHandleClick = this.handleClick.bind(this);
|
|
@@ -3130,8 +3204,13 @@ class FigTabs extends HTMLElement {
|
|
|
3130
3204
|
connectedCallback() {
|
|
3131
3205
|
this.name = this.getAttribute("name") || "tabs";
|
|
3132
3206
|
this.setAttribute("role", "tablist");
|
|
3207
|
+
this.#ensureScroller();
|
|
3133
3208
|
this.addEventListener("click", this.#boundHandleClick);
|
|
3134
3209
|
this.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
3210
|
+
this.#scrollElement.addEventListener("scroll", this.#boundSyncOverflow);
|
|
3211
|
+
this.#createNavButtons();
|
|
3212
|
+
this.#startObserver();
|
|
3213
|
+
this.#startResizeObserver();
|
|
3135
3214
|
requestAnimationFrame(() => {
|
|
3136
3215
|
const value = this.getAttribute("value");
|
|
3137
3216
|
if (value) {
|
|
@@ -3140,9 +3219,36 @@ class FigTabs extends HTMLElement {
|
|
|
3140
3219
|
if (this.hasAttribute("disabled")) {
|
|
3141
3220
|
this.#applyDisabled(true);
|
|
3142
3221
|
}
|
|
3222
|
+
this.#syncTabIndexes();
|
|
3223
|
+
this.#syncOverflow();
|
|
3224
|
+
this.#scrollSelectedTabIntoView(undefined, "auto");
|
|
3143
3225
|
});
|
|
3144
3226
|
}
|
|
3145
3227
|
|
|
3228
|
+
get #scrollElement() {
|
|
3229
|
+
return this.#scroller || this;
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
get scrollLeft() {
|
|
3233
|
+
return this.#scrollElement.scrollLeft;
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
set scrollLeft(value) {
|
|
3237
|
+
this.#scrollElement.scrollLeft = value;
|
|
3238
|
+
}
|
|
3239
|
+
|
|
3240
|
+
get scrollWidth() {
|
|
3241
|
+
return this.#scrollElement.scrollWidth;
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
scrollTo(...args) {
|
|
3245
|
+
this.#scrollElement.scrollTo(...args);
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
scrollBy(...args) {
|
|
3249
|
+
this.#scrollElement.scrollBy(...args);
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3146
3252
|
#applyDisabled(disabled) {
|
|
3147
3253
|
const tabs = this.querySelectorAll("fig-tab");
|
|
3148
3254
|
tabs.forEach((tab) => {
|
|
@@ -3173,9 +3279,136 @@ class FigTabs extends HTMLElement {
|
|
|
3173
3279
|
});
|
|
3174
3280
|
}
|
|
3175
3281
|
|
|
3282
|
+
#scrollSelectedTabIntoView(tab = this.selectedTab, behavior = "smooth") {
|
|
3283
|
+
const target =
|
|
3284
|
+
tab ||
|
|
3285
|
+
this.querySelector('fig-tab[selected]:not([selected="false"])') ||
|
|
3286
|
+
this.#availableTabs()[0];
|
|
3287
|
+
if (!target) return;
|
|
3288
|
+
|
|
3289
|
+
requestAnimationFrame(() => {
|
|
3290
|
+
if (!this.isConnected || !target.isConnected) return;
|
|
3291
|
+
const scrollEl = this.#scrollElement;
|
|
3292
|
+
const overflowX = scrollEl.scrollWidth > scrollEl.clientWidth;
|
|
3293
|
+
if (!overflowX) return;
|
|
3294
|
+
|
|
3295
|
+
const containerRect = scrollEl.getBoundingClientRect();
|
|
3296
|
+
const tabRect = target.getBoundingClientRect();
|
|
3297
|
+
const tabCenter =
|
|
3298
|
+
tabRect.left - containerRect.left + scrollEl.scrollLeft + tabRect.width / 2;
|
|
3299
|
+
|
|
3300
|
+
scrollEl.scrollTo({
|
|
3301
|
+
left: tabCenter - scrollEl.clientWidth / 2,
|
|
3302
|
+
behavior,
|
|
3303
|
+
});
|
|
3304
|
+
this.#syncOverflow();
|
|
3305
|
+
});
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3176
3308
|
disconnectedCallback() {
|
|
3177
3309
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
3178
3310
|
this.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
3311
|
+
this.#scrollElement.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
3312
|
+
this.#mutationObserver?.disconnect();
|
|
3313
|
+
this.#mutationObserver = null;
|
|
3314
|
+
this.#resizeObserver?.disconnect();
|
|
3315
|
+
this.#resizeObserver = null;
|
|
3316
|
+
this.#removeNavButtons();
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
#ensureScroller() {
|
|
3320
|
+
if (this.#isStructuring) return;
|
|
3321
|
+
const existing = this.querySelector(":scope > [data-fig-tabs-scroll]");
|
|
3322
|
+
if (existing && existing === this.#scroller) return;
|
|
3323
|
+
|
|
3324
|
+
this.#isStructuring = true;
|
|
3325
|
+
try {
|
|
3326
|
+
const previousScroller = this.#scroller;
|
|
3327
|
+
if (previousScroller && previousScroller !== existing) {
|
|
3328
|
+
previousScroller.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
const scroller = existing || document.createElement("div");
|
|
3332
|
+
scroller.className = "fig-tabs-scroll";
|
|
3333
|
+
scroller.dataset.figTabsScroll = "";
|
|
3334
|
+
|
|
3335
|
+
const nodes = Array.from(this.childNodes).filter((node) => {
|
|
3336
|
+
if (node === scroller) return false;
|
|
3337
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return true;
|
|
3338
|
+
return !node.hasAttribute("data-fig-tabs-nav");
|
|
3339
|
+
});
|
|
3340
|
+
|
|
3341
|
+
for (const node of nodes) {
|
|
3342
|
+
scroller.appendChild(node);
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3345
|
+
if (!existing) this.prepend(scroller);
|
|
3346
|
+
|
|
3347
|
+
this.#scroller = scroller;
|
|
3348
|
+
this.#scroller.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
3349
|
+
this.#scroller.addEventListener("scroll", this.#boundSyncOverflow);
|
|
3350
|
+
if (this.#resizeObserver) this.#resizeObserver.observe(this.#scroller);
|
|
3351
|
+
} finally {
|
|
3352
|
+
this.#isStructuring = false;
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
#syncOverflow() {
|
|
3357
|
+
figSyncOverflowState(this, this.#scrollElement, "x");
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
#startResizeObserver() {
|
|
3361
|
+
this.#resizeObserver?.disconnect();
|
|
3362
|
+
this.#resizeObserver = new ResizeObserver(() => this.#syncOverflow());
|
|
3363
|
+
this.#resizeObserver.observe(this);
|
|
3364
|
+
if (this.#scroller) this.#resizeObserver.observe(this.#scroller);
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
#startObserver() {
|
|
3368
|
+
this.#mutationObserver?.disconnect();
|
|
3369
|
+
this.#mutationObserver = new MutationObserver(() => {
|
|
3370
|
+
if (this.#isStructuring) return;
|
|
3371
|
+
this.#ensureScroller();
|
|
3372
|
+
this.#createNavButtons();
|
|
3373
|
+
this.#syncTabIndexes();
|
|
3374
|
+
requestAnimationFrame(() => {
|
|
3375
|
+
this.#syncOverflow();
|
|
3376
|
+
this.#scrollSelectedTabIntoView();
|
|
3377
|
+
});
|
|
3378
|
+
});
|
|
3379
|
+
this.#mutationObserver.observe(this, { childList: true, subtree: true });
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
#removeNavButtons() {
|
|
3383
|
+
this.#navStart?.remove();
|
|
3384
|
+
this.#navEnd?.remove();
|
|
3385
|
+
this.#navStart = null;
|
|
3386
|
+
this.#navEnd = null;
|
|
3387
|
+
this.classList.remove("overflow-start", "overflow-end");
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
#createNavButtons() {
|
|
3391
|
+
if (
|
|
3392
|
+
this.#navStart &&
|
|
3393
|
+
this.#navEnd &&
|
|
3394
|
+
this.contains(this.#navStart) &&
|
|
3395
|
+
this.contains(this.#navEnd)
|
|
3396
|
+
) {
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3399
|
+
|
|
3400
|
+
this.#navStart?.remove();
|
|
3401
|
+
this.#navEnd?.remove();
|
|
3402
|
+
|
|
3403
|
+
const buttons = createFigOverflowButtons({
|
|
3404
|
+
owner: "tabs",
|
|
3405
|
+
onStart: () => figScrollOverflowPage(this.#scrollElement, "x", -1),
|
|
3406
|
+
onEnd: () => figScrollOverflowPage(this.#scrollElement, "x", 1),
|
|
3407
|
+
});
|
|
3408
|
+
this.#navStart = buttons.start;
|
|
3409
|
+
this.#navEnd = buttons.end;
|
|
3410
|
+
this.prepend(this.#navStart);
|
|
3411
|
+
this.append(this.#navEnd);
|
|
3179
3412
|
}
|
|
3180
3413
|
|
|
3181
3414
|
#handleKeyDown(event) {
|
|
@@ -3215,6 +3448,7 @@ class FigTabs extends HTMLElement {
|
|
|
3215
3448
|
if (val) this.setAttribute("value", val);
|
|
3216
3449
|
tabs[newIndex].focus();
|
|
3217
3450
|
this.#syncTabIndexes();
|
|
3451
|
+
this.#scrollSelectedTabIntoView(tabs[newIndex]);
|
|
3218
3452
|
}
|
|
3219
3453
|
}
|
|
3220
3454
|
|
|
@@ -3237,6 +3471,7 @@ class FigTabs extends HTMLElement {
|
|
|
3237
3471
|
}
|
|
3238
3472
|
}
|
|
3239
3473
|
this.#syncTabIndexes();
|
|
3474
|
+
this.#scrollSelectedTabIntoView(this.selectedTab);
|
|
3240
3475
|
}
|
|
3241
3476
|
|
|
3242
3477
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
@@ -3268,6 +3503,7 @@ class FigTabs extends HTMLElement {
|
|
|
3268
3503
|
const val = target.getAttribute("value");
|
|
3269
3504
|
if (val) this.setAttribute("value", val);
|
|
3270
3505
|
this.#syncTabIndexes();
|
|
3506
|
+
this.#scrollSelectedTabIntoView(target);
|
|
3271
3507
|
}
|
|
3272
3508
|
}
|
|
3273
3509
|
customElements.define("fig-tabs", FigTabs);
|
|
@@ -14011,9 +14247,11 @@ class FigChooser extends HTMLElement {
|
|
|
14011
14247
|
#boundSyncOverflow = this.#syncOverflow.bind(this);
|
|
14012
14248
|
#mutationObserver = null;
|
|
14013
14249
|
#resizeObserver = null;
|
|
14250
|
+
#scroller = null;
|
|
14014
14251
|
#navStart = null;
|
|
14015
14252
|
#navEnd = null;
|
|
14016
14253
|
#dragState = null;
|
|
14254
|
+
#isStructuring = false;
|
|
14017
14255
|
|
|
14018
14256
|
constructor() {
|
|
14019
14257
|
super();
|
|
@@ -14046,7 +14284,43 @@ class FigChooser extends HTMLElement {
|
|
|
14046
14284
|
}
|
|
14047
14285
|
|
|
14048
14286
|
get choices() {
|
|
14049
|
-
return Array.from(this.querySelectorAll(this.#choiceSelector));
|
|
14287
|
+
return Array.from((this.#scroller || this).querySelectorAll(this.#choiceSelector));
|
|
14288
|
+
}
|
|
14289
|
+
|
|
14290
|
+
get #scrollElement() {
|
|
14291
|
+
return this.#scroller || this;
|
|
14292
|
+
}
|
|
14293
|
+
|
|
14294
|
+
get scrollTop() {
|
|
14295
|
+
return this.#scrollElement.scrollTop;
|
|
14296
|
+
}
|
|
14297
|
+
|
|
14298
|
+
set scrollTop(value) {
|
|
14299
|
+
this.#scrollElement.scrollTop = value;
|
|
14300
|
+
}
|
|
14301
|
+
|
|
14302
|
+
get scrollLeft() {
|
|
14303
|
+
return this.#scrollElement.scrollLeft;
|
|
14304
|
+
}
|
|
14305
|
+
|
|
14306
|
+
set scrollLeft(value) {
|
|
14307
|
+
this.#scrollElement.scrollLeft = value;
|
|
14308
|
+
}
|
|
14309
|
+
|
|
14310
|
+
get scrollWidth() {
|
|
14311
|
+
return this.#scrollElement.scrollWidth;
|
|
14312
|
+
}
|
|
14313
|
+
|
|
14314
|
+
get scrollHeight() {
|
|
14315
|
+
return this.#scrollElement.scrollHeight;
|
|
14316
|
+
}
|
|
14317
|
+
|
|
14318
|
+
scrollTo(...args) {
|
|
14319
|
+
this.#scrollElement.scrollTo(...args);
|
|
14320
|
+
}
|
|
14321
|
+
|
|
14322
|
+
scrollBy(...args) {
|
|
14323
|
+
this.#scrollElement.scrollBy(...args);
|
|
14050
14324
|
}
|
|
14051
14325
|
|
|
14052
14326
|
get selectedChoice() {
|
|
@@ -14086,9 +14360,10 @@ class FigChooser extends HTMLElement {
|
|
|
14086
14360
|
|
|
14087
14361
|
connectedCallback() {
|
|
14088
14362
|
this.setAttribute("role", "listbox");
|
|
14363
|
+
this.#ensureScroller();
|
|
14089
14364
|
this.addEventListener("click", this.#boundHandleClick);
|
|
14090
14365
|
this.addEventListener("keydown", this.#boundHandleKeyDown);
|
|
14091
|
-
this.addEventListener("scroll", this.#boundSyncOverflow);
|
|
14366
|
+
this.#scrollElement.addEventListener("scroll", this.#boundSyncOverflow);
|
|
14092
14367
|
this.#applyOverflowMode();
|
|
14093
14368
|
this.#setupDrag();
|
|
14094
14369
|
this.#startObserver();
|
|
@@ -14132,7 +14407,7 @@ class FigChooser extends HTMLElement {
|
|
|
14132
14407
|
disconnectedCallback() {
|
|
14133
14408
|
this.removeEventListener("click", this.#boundHandleClick);
|
|
14134
14409
|
this.removeEventListener("keydown", this.#boundHandleKeyDown);
|
|
14135
|
-
this.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
14410
|
+
this.#scrollElement.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
14136
14411
|
this.#teardownDrag();
|
|
14137
14412
|
this.#mutationObserver?.disconnect();
|
|
14138
14413
|
this.#mutationObserver = null;
|
|
@@ -14308,25 +14583,7 @@ class FigChooser extends HTMLElement {
|
|
|
14308
14583
|
#syncOverflow() {
|
|
14309
14584
|
if (this.#overflowMode === "scrollbar") return;
|
|
14310
14585
|
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
14311
|
-
|
|
14312
|
-
|
|
14313
|
-
if (isHorizontal) {
|
|
14314
|
-
const scrollable = this.scrollWidth - this.clientWidth > threshold;
|
|
14315
|
-
const atStart = !scrollable || this.scrollLeft <= threshold;
|
|
14316
|
-
const atEnd =
|
|
14317
|
-
!scrollable ||
|
|
14318
|
-
this.scrollLeft + this.clientWidth >= this.scrollWidth - threshold;
|
|
14319
|
-
this.classList.toggle("overflow-start", !atStart);
|
|
14320
|
-
this.classList.toggle("overflow-end", !atEnd);
|
|
14321
|
-
} else {
|
|
14322
|
-
const scrollable = this.scrollHeight - this.clientHeight > threshold;
|
|
14323
|
-
const atStart = !scrollable || this.scrollTop <= threshold;
|
|
14324
|
-
const atEnd =
|
|
14325
|
-
!scrollable ||
|
|
14326
|
-
this.scrollTop + this.clientHeight >= this.scrollHeight - threshold;
|
|
14327
|
-
this.classList.toggle("overflow-start", !atStart);
|
|
14328
|
-
this.classList.toggle("overflow-end", !atEnd);
|
|
14329
|
-
}
|
|
14586
|
+
figSyncOverflowState(this, this.#scrollElement, isHorizontal ? "x" : "y");
|
|
14330
14587
|
}
|
|
14331
14588
|
|
|
14332
14589
|
#startResizeObserver() {
|
|
@@ -14335,28 +14592,30 @@ class FigChooser extends HTMLElement {
|
|
|
14335
14592
|
this.#syncOverflow();
|
|
14336
14593
|
});
|
|
14337
14594
|
this.#resizeObserver.observe(this);
|
|
14595
|
+
if (this.#scroller) this.#resizeObserver.observe(this.#scroller);
|
|
14338
14596
|
}
|
|
14339
14597
|
|
|
14340
14598
|
#setupDrag() {
|
|
14341
14599
|
if (this.#dragState?.bound) return;
|
|
14342
14600
|
if (!this.#dragEnabled) return;
|
|
14601
|
+
const scrollEl = this.#scrollElement;
|
|
14343
14602
|
|
|
14344
14603
|
const onPointerDown = (e) => {
|
|
14345
14604
|
if (e.button !== 0) return;
|
|
14346
14605
|
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
14347
14606
|
const hasOverflow = isHorizontal
|
|
14348
|
-
?
|
|
14349
|
-
:
|
|
14607
|
+
? scrollEl.scrollWidth > scrollEl.clientWidth
|
|
14608
|
+
: scrollEl.scrollHeight > scrollEl.clientHeight;
|
|
14350
14609
|
if (!hasOverflow) return;
|
|
14351
14610
|
|
|
14352
14611
|
this.#dragState.active = true;
|
|
14353
14612
|
this.#dragState.didDrag = false;
|
|
14354
14613
|
this.#dragState.startX = e.clientX;
|
|
14355
14614
|
this.#dragState.startY = e.clientY;
|
|
14356
|
-
this.#dragState.scrollLeft =
|
|
14357
|
-
this.#dragState.scrollTop =
|
|
14358
|
-
|
|
14359
|
-
|
|
14615
|
+
this.#dragState.scrollLeft = scrollEl.scrollLeft;
|
|
14616
|
+
this.#dragState.scrollTop = scrollEl.scrollTop;
|
|
14617
|
+
scrollEl.style.cursor = "grab";
|
|
14618
|
+
scrollEl.style.userSelect = "none";
|
|
14360
14619
|
};
|
|
14361
14620
|
|
|
14362
14621
|
const onPointerMove = (e) => {
|
|
@@ -14367,16 +14626,16 @@ class FigChooser extends HTMLElement {
|
|
|
14367
14626
|
|
|
14368
14627
|
if (!this.#dragState.didDrag && Math.abs(isHorizontal ? dx : dy) > 3) {
|
|
14369
14628
|
this.#dragState.didDrag = true;
|
|
14370
|
-
|
|
14371
|
-
|
|
14629
|
+
scrollEl.style.cursor = "grabbing";
|
|
14630
|
+
scrollEl.setPointerCapture(e.pointerId);
|
|
14372
14631
|
}
|
|
14373
14632
|
|
|
14374
14633
|
if (!this.#dragState.didDrag) return;
|
|
14375
14634
|
|
|
14376
14635
|
if (isHorizontal) {
|
|
14377
|
-
|
|
14636
|
+
scrollEl.scrollLeft = this.#dragState.scrollLeft - dx;
|
|
14378
14637
|
} else {
|
|
14379
|
-
|
|
14638
|
+
scrollEl.scrollTop = this.#dragState.scrollTop - dy;
|
|
14380
14639
|
}
|
|
14381
14640
|
};
|
|
14382
14641
|
|
|
@@ -14385,11 +14644,11 @@ class FigChooser extends HTMLElement {
|
|
|
14385
14644
|
const wasDrag = this.#dragState.didDrag;
|
|
14386
14645
|
this.#dragState.active = false;
|
|
14387
14646
|
this.#dragState.didDrag = false;
|
|
14388
|
-
|
|
14389
|
-
|
|
14647
|
+
scrollEl.style.cursor = "";
|
|
14648
|
+
scrollEl.style.userSelect = "";
|
|
14390
14649
|
if (e.pointerId !== undefined) {
|
|
14391
14650
|
try {
|
|
14392
|
-
|
|
14651
|
+
scrollEl.releasePointerCapture(e.pointerId);
|
|
14393
14652
|
} catch {}
|
|
14394
14653
|
}
|
|
14395
14654
|
if (wasDrag) {
|
|
@@ -14429,9 +14688,10 @@ class FigChooser extends HTMLElement {
|
|
|
14429
14688
|
onPointerUp,
|
|
14430
14689
|
onClick,
|
|
14431
14690
|
onPointerUpCapture,
|
|
14691
|
+
scrollEl,
|
|
14432
14692
|
};
|
|
14433
14693
|
|
|
14434
|
-
|
|
14694
|
+
scrollEl.addEventListener("pointerdown", onPointerDown);
|
|
14435
14695
|
window.addEventListener("pointermove", onPointerMove);
|
|
14436
14696
|
window.addEventListener("pointerup", onPointerUp);
|
|
14437
14697
|
this.addEventListener("pointerup", onPointerUpCapture, true);
|
|
@@ -14440,7 +14700,7 @@ class FigChooser extends HTMLElement {
|
|
|
14440
14700
|
|
|
14441
14701
|
#teardownDrag() {
|
|
14442
14702
|
if (!this.#dragState?.bound) return;
|
|
14443
|
-
this.removeEventListener("pointerdown", this.#dragState.onPointerDown);
|
|
14703
|
+
this.#dragState.scrollEl.removeEventListener("pointerdown", this.#dragState.onPointerDown);
|
|
14444
14704
|
window.removeEventListener("pointermove", this.#dragState.onPointerMove);
|
|
14445
14705
|
window.removeEventListener("pointerup", this.#dragState.onPointerUp);
|
|
14446
14706
|
this.removeEventListener(
|
|
@@ -14449,12 +14709,52 @@ class FigChooser extends HTMLElement {
|
|
|
14449
14709
|
true,
|
|
14450
14710
|
);
|
|
14451
14711
|
this.removeEventListener("click", this.#dragState.onClick, true);
|
|
14452
|
-
this.style.cursor = "";
|
|
14453
|
-
this.style.userSelect = "";
|
|
14712
|
+
this.#dragState.scrollEl.style.cursor = "";
|
|
14713
|
+
this.#dragState.scrollEl.style.userSelect = "";
|
|
14454
14714
|
this.#dragState = null;
|
|
14455
14715
|
}
|
|
14456
14716
|
|
|
14717
|
+
#ensureScroller() {
|
|
14718
|
+
if (this.#isStructuring) return;
|
|
14719
|
+
const existing = this.querySelector(":scope > [data-fig-chooser-scroll]");
|
|
14720
|
+
if (existing && existing === this.#scroller) return;
|
|
14721
|
+
|
|
14722
|
+
this.#isStructuring = true;
|
|
14723
|
+
try {
|
|
14724
|
+
const previousScroller = this.#scroller;
|
|
14725
|
+
if (previousScroller && previousScroller !== existing) {
|
|
14726
|
+
previousScroller.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
14727
|
+
}
|
|
14728
|
+
|
|
14729
|
+
const scroller = existing || document.createElement("div");
|
|
14730
|
+
scroller.className = "fig-chooser-scroll";
|
|
14731
|
+
scroller.dataset.figChooserScroll = "";
|
|
14732
|
+
|
|
14733
|
+
const nodes = Array.from(this.childNodes).filter((node) => {
|
|
14734
|
+
if (node === scroller) return false;
|
|
14735
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return true;
|
|
14736
|
+
return !node.hasAttribute("data-fig-chooser-nav");
|
|
14737
|
+
});
|
|
14738
|
+
|
|
14739
|
+
for (const node of nodes) {
|
|
14740
|
+
scroller.appendChild(node);
|
|
14741
|
+
}
|
|
14742
|
+
|
|
14743
|
+
if (!existing) {
|
|
14744
|
+
this.prepend(scroller);
|
|
14745
|
+
}
|
|
14746
|
+
|
|
14747
|
+
this.#scroller = scroller;
|
|
14748
|
+
this.#scroller.removeEventListener("scroll", this.#boundSyncOverflow);
|
|
14749
|
+
this.#scroller.addEventListener("scroll", this.#boundSyncOverflow);
|
|
14750
|
+
if (this.#resizeObserver) this.#resizeObserver.observe(this.#scroller);
|
|
14751
|
+
} finally {
|
|
14752
|
+
this.#isStructuring = false;
|
|
14753
|
+
}
|
|
14754
|
+
}
|
|
14755
|
+
|
|
14457
14756
|
#applyOverflowMode() {
|
|
14757
|
+
this.#ensureScroller();
|
|
14458
14758
|
if (this.#overflowMode === "scrollbar") {
|
|
14459
14759
|
this.#removeNavButtons();
|
|
14460
14760
|
} else {
|
|
@@ -14471,36 +14771,30 @@ class FigChooser extends HTMLElement {
|
|
|
14471
14771
|
}
|
|
14472
14772
|
|
|
14473
14773
|
#createNavButtons() {
|
|
14474
|
-
if (
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
14478
|
-
|
|
14479
|
-
|
|
14480
|
-
|
|
14481
|
-
}
|
|
14482
|
-
|
|
14483
|
-
this.#navStart = document.createElement("button");
|
|
14484
|
-
this.#navStart.className = "fig-chooser-nav-start";
|
|
14485
|
-
this.#navStart.setAttribute("tabindex", "-1");
|
|
14486
|
-
this.#navStart.setAttribute("aria-label", "Scroll back");
|
|
14487
|
-
this.#navStart.appendChild(makeChevron());
|
|
14488
|
-
|
|
14489
|
-
this.#navEnd = document.createElement("button");
|
|
14490
|
-
this.#navEnd.className = "fig-chooser-nav-end";
|
|
14491
|
-
this.#navEnd.setAttribute("tabindex", "-1");
|
|
14492
|
-
this.#navEnd.setAttribute("aria-label", "Scroll forward");
|
|
14493
|
-
this.#navEnd.appendChild(makeChevron());
|
|
14774
|
+
if (
|
|
14775
|
+
this.#navStart &&
|
|
14776
|
+
this.#navEnd &&
|
|
14777
|
+
this.contains(this.#navStart) &&
|
|
14778
|
+
this.contains(this.#navEnd)
|
|
14779
|
+
) {
|
|
14780
|
+
return;
|
|
14781
|
+
}
|
|
14494
14782
|
|
|
14495
|
-
this.#navStart
|
|
14496
|
-
|
|
14497
|
-
|
|
14498
|
-
|
|
14783
|
+
this.#navStart?.remove();
|
|
14784
|
+
this.#navEnd?.remove();
|
|
14785
|
+
this.#navStart = null;
|
|
14786
|
+
this.#navEnd = null;
|
|
14499
14787
|
|
|
14500
|
-
|
|
14501
|
-
|
|
14502
|
-
|
|
14788
|
+
const buttons = createFigOverflowButtons({
|
|
14789
|
+
owner: "chooser",
|
|
14790
|
+
startClass: "fig-chooser-nav-start",
|
|
14791
|
+
endClass: "fig-chooser-nav-end",
|
|
14792
|
+
chevronClass: "fig-chooser-nav-chevron",
|
|
14793
|
+
onStart: () => this.#scrollByPage(-1),
|
|
14794
|
+
onEnd: () => this.#scrollByPage(1),
|
|
14503
14795
|
});
|
|
14796
|
+
this.#navStart = buttons.start;
|
|
14797
|
+
this.#navEnd = buttons.end;
|
|
14504
14798
|
|
|
14505
14799
|
this.prepend(this.#navStart);
|
|
14506
14800
|
this.append(this.#navEnd);
|
|
@@ -14508,49 +14802,48 @@ class FigChooser extends HTMLElement {
|
|
|
14508
14802
|
|
|
14509
14803
|
#scrollByPage(direction) {
|
|
14510
14804
|
const isHorizontal = this.getAttribute("layout") === "horizontal";
|
|
14511
|
-
const
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
this.scrollBy({
|
|
14515
|
-
[isHorizontal ? "left" : "top"]: scrollAmount,
|
|
14516
|
-
behavior: "smooth",
|
|
14517
|
-
});
|
|
14805
|
+
const scrollEl = this.#scrollElement;
|
|
14806
|
+
figScrollOverflowPage(scrollEl, isHorizontal ? "x" : "y", direction);
|
|
14518
14807
|
}
|
|
14519
14808
|
|
|
14520
14809
|
#scrollToChoice(el, behavior = "smooth") {
|
|
14521
14810
|
if (!el) return;
|
|
14522
14811
|
requestAnimationFrame(() => {
|
|
14523
14812
|
if (!el.isConnected) return;
|
|
14524
|
-
const
|
|
14525
|
-
const
|
|
14813
|
+
const scrollEl = this.#scrollElement;
|
|
14814
|
+
const overflowY = scrollEl.scrollHeight > scrollEl.clientHeight;
|
|
14815
|
+
const overflowX = scrollEl.scrollWidth > scrollEl.clientWidth;
|
|
14526
14816
|
if (!overflowX && !overflowY) return;
|
|
14527
14817
|
|
|
14528
14818
|
const choiceRect = el.getBoundingClientRect();
|
|
14529
|
-
const hostRect =
|
|
14819
|
+
const hostRect = scrollEl.getBoundingClientRect();
|
|
14530
14820
|
const options = { behavior };
|
|
14531
14821
|
|
|
14532
14822
|
if (overflowY) {
|
|
14533
14823
|
const choiceCenter =
|
|
14534
|
-
choiceRect.top - hostRect.top +
|
|
14535
|
-
options.top = choiceCenter -
|
|
14824
|
+
choiceRect.top - hostRect.top + scrollEl.scrollTop + choiceRect.height / 2;
|
|
14825
|
+
options.top = choiceCenter - scrollEl.clientHeight / 2;
|
|
14536
14826
|
}
|
|
14537
14827
|
|
|
14538
14828
|
if (overflowX) {
|
|
14539
14829
|
const choiceCenter =
|
|
14540
14830
|
choiceRect.left -
|
|
14541
14831
|
hostRect.left +
|
|
14542
|
-
|
|
14832
|
+
scrollEl.scrollLeft +
|
|
14543
14833
|
choiceRect.width / 2;
|
|
14544
|
-
options.left = choiceCenter -
|
|
14834
|
+
options.left = choiceCenter - scrollEl.clientWidth / 2;
|
|
14545
14835
|
}
|
|
14546
14836
|
|
|
14547
|
-
|
|
14837
|
+
scrollEl.scrollTo(options);
|
|
14548
14838
|
});
|
|
14549
14839
|
}
|
|
14550
14840
|
|
|
14551
14841
|
#startObserver() {
|
|
14552
14842
|
this.#mutationObserver?.disconnect();
|
|
14553
14843
|
this.#mutationObserver = new MutationObserver(() => {
|
|
14844
|
+
if (this.#isStructuring) return;
|
|
14845
|
+
this.#ensureScroller();
|
|
14846
|
+
this.#applyOverflowMode();
|
|
14554
14847
|
const choices = this.choices;
|
|
14555
14848
|
if (this.#selectedChoice && !choices.includes(this.#selectedChoice)) {
|
|
14556
14849
|
this.#selectedChoice = null;
|
|
@@ -14558,6 +14851,7 @@ class FigChooser extends HTMLElement {
|
|
|
14558
14851
|
} else if (!this.#selectedChoice && choices.length) {
|
|
14559
14852
|
this.#syncSelection();
|
|
14560
14853
|
}
|
|
14854
|
+
requestAnimationFrame(() => this.#syncOverflow());
|
|
14561
14855
|
});
|
|
14562
14856
|
this.#mutationObserver.observe(this, { childList: true, subtree: true });
|
|
14563
14857
|
}
|
package/package.json
CHANGED