@vesture/react 0.2.8 → 0.2.9
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/index.css +8 -2
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +79 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3246,14 +3246,17 @@ import {
|
|
|
3246
3246
|
var chip = "Combobox_chip__26nhic2";
|
|
3247
3247
|
var chipRemove = "Combobox_chipRemove__26nhic3";
|
|
3248
3248
|
var chipsWrapper = "Combobox_chipsWrapper__26nhic1";
|
|
3249
|
-
var emptyState2 = "
|
|
3249
|
+
var emptyState2 = "Combobox_emptyState__26nhic8";
|
|
3250
3250
|
var inputEl = "Combobox_inputEl__26nhic4";
|
|
3251
3251
|
var listbox = "Combobox_listbox__26nhic5";
|
|
3252
3252
|
var option = "Combobox_option__26nhic6";
|
|
3253
|
+
var virtualSpacer = "Combobox_virtualSpacer__26nhic7";
|
|
3253
3254
|
var wrapper9 = "Combobox_wrapper__26nhic0";
|
|
3254
3255
|
|
|
3255
3256
|
// src/components/Combobox/Combobox.tsx
|
|
3256
3257
|
import { jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
3258
|
+
var DEFAULT_OPTION_HEIGHT = 37;
|
|
3259
|
+
var OVERSCAN = 5;
|
|
3257
3260
|
function defaultFilter(options, inputText) {
|
|
3258
3261
|
const query = inputText.trim().toLowerCase();
|
|
3259
3262
|
if (!query) return options;
|
|
@@ -3276,6 +3279,8 @@ function Combobox({
|
|
|
3276
3279
|
disabled = false,
|
|
3277
3280
|
invalid,
|
|
3278
3281
|
loading = false,
|
|
3282
|
+
virtualizationThreshold = 50,
|
|
3283
|
+
optionHeight = DEFAULT_OPTION_HEIGHT,
|
|
3279
3284
|
id,
|
|
3280
3285
|
className,
|
|
3281
3286
|
...rest
|
|
@@ -3302,8 +3307,37 @@ function Combobox({
|
|
|
3302
3307
|
}
|
|
3303
3308
|
}, [singleValue, multiple]);
|
|
3304
3309
|
const inputRef = useRef9(null);
|
|
3310
|
+
const listboxRef = useRef9(null);
|
|
3305
3311
|
const listboxId = useId2();
|
|
3312
|
+
const [scrollTop, setScrollTop] = useState14(0);
|
|
3313
|
+
const [viewportHeight, setViewportHeight] = useState14(280);
|
|
3306
3314
|
const filteredOptions = filterOptions(options, multiple ? inputText : open ? inputText : "");
|
|
3315
|
+
const isVirtualized = filteredOptions.length >= virtualizationThreshold;
|
|
3316
|
+
useEffect7(() => {
|
|
3317
|
+
setScrollTop(0);
|
|
3318
|
+
setActiveIndex(null);
|
|
3319
|
+
if (listboxRef.current) listboxRef.current.scrollTop = 0;
|
|
3320
|
+
}, [options]);
|
|
3321
|
+
useEffect7(() => {
|
|
3322
|
+
if (!open) return;
|
|
3323
|
+
const node = listboxRef.current;
|
|
3324
|
+
if (!node) return;
|
|
3325
|
+
if (node.clientHeight > 0) setViewportHeight(node.clientHeight);
|
|
3326
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
3327
|
+
const observer = new ResizeObserver((entries) => {
|
|
3328
|
+
const entry = entries[0];
|
|
3329
|
+
if (entry) setViewportHeight(entry.contentRect.height);
|
|
3330
|
+
});
|
|
3331
|
+
observer.observe(node);
|
|
3332
|
+
return () => observer.disconnect();
|
|
3333
|
+
}, [open]);
|
|
3334
|
+
const totalHeight = filteredOptions.length * optionHeight;
|
|
3335
|
+
const maxScrollTop = Math.max(0, totalHeight - viewportHeight);
|
|
3336
|
+
const effectiveScrollTop = Math.min(scrollTop, maxScrollTop);
|
|
3337
|
+
const visibleCount = Math.ceil(viewportHeight / optionHeight) + OVERSCAN * 2;
|
|
3338
|
+
const startIndex = isVirtualized ? Math.max(0, Math.floor(effectiveScrollTop / optionHeight) - OVERSCAN) : 0;
|
|
3339
|
+
const endIndex = isVirtualized ? Math.min(filteredOptions.length, startIndex + visibleCount) : filteredOptions.length;
|
|
3340
|
+
const visibleOptions = filteredOptions.slice(startIndex, endIndex);
|
|
3307
3341
|
const { refs, floatingStyles, context } = useFloating7({
|
|
3308
3342
|
open,
|
|
3309
3343
|
onOpenChange: setOpen,
|
|
@@ -3348,6 +3382,21 @@ function Combobox({
|
|
|
3348
3382
|
setOpen(true);
|
|
3349
3383
|
setActiveIndex(null);
|
|
3350
3384
|
};
|
|
3385
|
+
const scrollIndexIntoView = (index) => {
|
|
3386
|
+
if (!isVirtualized) return;
|
|
3387
|
+
const rowTop = index * optionHeight;
|
|
3388
|
+
const rowBottom = rowTop + optionHeight;
|
|
3389
|
+
let next = scrollTop;
|
|
3390
|
+
if (rowTop < scrollTop) {
|
|
3391
|
+
next = rowTop;
|
|
3392
|
+
} else if (rowBottom > scrollTop + viewportHeight) {
|
|
3393
|
+
next = rowBottom - viewportHeight;
|
|
3394
|
+
} else {
|
|
3395
|
+
return;
|
|
3396
|
+
}
|
|
3397
|
+
setScrollTop(next);
|
|
3398
|
+
if (listboxRef.current) listboxRef.current.scrollTop = next;
|
|
3399
|
+
};
|
|
3351
3400
|
const moveActiveIndex = (direction2) => {
|
|
3352
3401
|
if (filteredOptions.length === 0) return;
|
|
3353
3402
|
let next = activeIndex ?? (direction2 === 1 ? -1 : filteredOptions.length);
|
|
@@ -3355,6 +3404,7 @@ function Combobox({
|
|
|
3355
3404
|
next = (next + direction2 + filteredOptions.length) % filteredOptions.length;
|
|
3356
3405
|
if (!filteredOptions[next]?.disabled) {
|
|
3357
3406
|
setActiveIndex(next);
|
|
3407
|
+
scrollIndexIntoView(next);
|
|
3358
3408
|
return;
|
|
3359
3409
|
}
|
|
3360
3410
|
}
|
|
@@ -3448,14 +3498,40 @@ function Combobox({
|
|
|
3448
3498
|
open ? /* @__PURE__ */ jsx40(FloatingPortal7, { children: /* @__PURE__ */ jsx40(
|
|
3449
3499
|
"div",
|
|
3450
3500
|
{
|
|
3451
|
-
ref:
|
|
3501
|
+
ref: (node) => {
|
|
3502
|
+
refs.setFloating(node);
|
|
3503
|
+
listboxRef.current = node;
|
|
3504
|
+
},
|
|
3452
3505
|
id: listboxId,
|
|
3453
3506
|
role: "listbox",
|
|
3454
3507
|
"aria-multiselectable": multiple || void 0,
|
|
3455
3508
|
className: listbox,
|
|
3456
3509
|
style: floatingStyles,
|
|
3510
|
+
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
3457
3511
|
...getFloatingProps(),
|
|
3458
|
-
children: loading ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: "Loading\u2026" }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: noOptionsMessage }) :
|
|
3512
|
+
children: loading ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: "Loading\u2026" }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx40("div", { className: emptyState2, children: noOptionsMessage }) : isVirtualized ? /* @__PURE__ */ jsx40("div", { className: virtualSpacer, style: { height: totalHeight }, children: visibleOptions.map((opt, i) => {
|
|
3513
|
+
const index = startIndex + i;
|
|
3514
|
+
const isSelected = multiple ? selectedValues.includes(opt.value) : opt.value === singleValue;
|
|
3515
|
+
return /* @__PURE__ */ jsx40(
|
|
3516
|
+
"div",
|
|
3517
|
+
{
|
|
3518
|
+
id: `${listboxId}-option-${index}`,
|
|
3519
|
+
role: "option",
|
|
3520
|
+
"aria-selected": isSelected,
|
|
3521
|
+
"aria-disabled": opt.disabled || void 0,
|
|
3522
|
+
"data-active": activeIndex === index || void 0,
|
|
3523
|
+
className: option,
|
|
3524
|
+
style: { position: "absolute", top: index * optionHeight, left: 0, right: 0, height: optionHeight },
|
|
3525
|
+
onMouseDown: (event) => {
|
|
3526
|
+
event.preventDefault();
|
|
3527
|
+
selectOption(opt);
|
|
3528
|
+
},
|
|
3529
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
3530
|
+
children: opt.label
|
|
3531
|
+
},
|
|
3532
|
+
opt.value
|
|
3533
|
+
);
|
|
3534
|
+
}) }) : filteredOptions.map((opt, index) => {
|
|
3459
3535
|
const isSelected = multiple ? selectedValues.includes(opt.value) : opt.value === singleValue;
|
|
3460
3536
|
return /* @__PURE__ */ jsx40(
|
|
3461
3537
|
"div",
|