@lensui/lens-table 0.1.76 → 0.1.77

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.cjs CHANGED
@@ -336,6 +336,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
336
336
  const editorRef = (0, import_react2.useRef)(null);
337
337
  const popupRef = (0, import_react2.useRef)(null);
338
338
  const activeOptionRef = (0, import_react2.useRef)(null);
339
+ const popupReadyRef = (0, import_react2.useRef)(false);
340
+ const navigationSourceRef = (0, import_react2.useRef)(null);
339
341
  const originalValueRef = (0, import_react2.useRef)(value);
340
342
  const listboxId = (0, import_react2.useId)();
341
343
  const originalValue = originalValueRef.current;
@@ -344,7 +346,10 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
344
346
  const selectedIndex = options.findIndex((option) => option.value === value);
345
347
  return selectedIndex >= 0 ? selectedIndex : 0;
346
348
  });
347
- const [popupStyle, setPopupStyle] = (0, import_react2.useState)({});
349
+ const [popupStyle, setPopupStyle] = (0, import_react2.useState)({
350
+ position: "fixed",
351
+ visibility: "hidden"
352
+ });
348
353
  const filteredOptions = (0, import_react2.useMemo)(() => {
349
354
  const query = inputValue.trim().toLocaleLowerCase();
350
355
  if (!query || query === originalValue.toLocaleLowerCase()) return options;
@@ -362,7 +367,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
362
367
  bottom: window.innerHeight - rect.top + 4,
363
368
  left: rect.left,
364
369
  width: rect.width,
365
- maxHeight: Math.min(maxDropdownHeight, availableAbove)
370
+ maxHeight: Math.min(maxDropdownHeight, availableAbove),
371
+ visibility: popupReadyRef.current ? "visible" : "hidden"
366
372
  });
367
373
  return;
368
374
  }
@@ -371,7 +377,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
371
377
  top: rect.bottom + 4,
372
378
  left: rect.left,
373
379
  width: rect.width,
374
- maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow))
380
+ maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow)),
381
+ visibility: popupReadyRef.current ? "visible" : "hidden"
375
382
  });
376
383
  };
377
384
  positionPopup();
@@ -383,8 +390,22 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
383
390
  };
384
391
  }, [maxDropdownHeight]);
385
392
  (0, import_react2.useLayoutEffect)(() => {
386
- activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
387
- }, [activeIndex, filteredOptions, popupStyle]);
393
+ if (popupReadyRef.current) return;
394
+ if (popupStyle.top === void 0 && popupStyle.bottom === void 0) return;
395
+ const selectedOption = popupRef.current?.querySelector('[aria-selected="true"]') ?? activeOptionRef.current;
396
+ selectedOption?.scrollIntoView?.({ block: "center", inline: "nearest" });
397
+ popupReadyRef.current = true;
398
+ setPopupStyle((current) => ({ ...current, visibility: "visible" }));
399
+ }, [popupStyle.bottom, popupStyle.top]);
400
+ (0, import_react2.useLayoutEffect)(() => {
401
+ if (!popupReadyRef.current) return;
402
+ if (navigationSourceRef.current === "keyboard") {
403
+ activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
404
+ } else if (navigationSourceRef.current === "filter" && popupRef.current) {
405
+ popupRef.current.scrollTop = 0;
406
+ }
407
+ navigationSourceRef.current = null;
408
+ }, [activeIndex, filteredOptions]);
388
409
  (0, import_react2.useEffect)(() => {
389
410
  const handleOutsidePointer = (event) => {
390
411
  const target = event.target;
@@ -404,10 +425,12 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
404
425
  if (event.key === "Escape") onCancel();
405
426
  if (event.key === "ArrowDown") {
406
427
  event.preventDefault();
428
+ navigationSourceRef.current = "keyboard";
407
429
  setActiveIndex((index) => Math.min(filteredOptions.length - 1, index + 1));
408
430
  }
409
431
  if (event.key === "ArrowUp") {
410
432
  event.preventDefault();
433
+ navigationSourceRef.current = "keyboard";
411
434
  setActiveIndex((index) => Math.max(0, index - 1));
412
435
  }
413
436
  if (event.key === "Enter") {
@@ -430,6 +453,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
430
453
  onChange: (event) => {
431
454
  setInputValue(event.target.value);
432
455
  onChange(event.target.value);
456
+ navigationSourceRef.current = "filter";
433
457
  setActiveIndex(0);
434
458
  },
435
459
  onKeyDown: handleKeyDown
@@ -445,7 +469,10 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
445
469
  "aria-selected": option.value === originalValue,
446
470
  className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === originalValue ? " is-selected" : ""}`,
447
471
  onPointerDown: (event) => event.preventDefault(),
448
- onMouseEnter: () => setActiveIndex(index),
472
+ onMouseEnter: () => {
473
+ navigationSourceRef.current = "pointer";
474
+ setActiveIndex(index);
475
+ },
449
476
  onClick: () => selectOption(option),
450
477
  children: [
451
478
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: option.label }),
package/dist/index.js CHANGED
@@ -306,6 +306,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
306
306
  const editorRef = useRef(null);
307
307
  const popupRef = useRef(null);
308
308
  const activeOptionRef = useRef(null);
309
+ const popupReadyRef = useRef(false);
310
+ const navigationSourceRef = useRef(null);
309
311
  const originalValueRef = useRef(value);
310
312
  const listboxId = useId2();
311
313
  const originalValue = originalValueRef.current;
@@ -314,7 +316,10 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
314
316
  const selectedIndex = options.findIndex((option) => option.value === value);
315
317
  return selectedIndex >= 0 ? selectedIndex : 0;
316
318
  });
317
- const [popupStyle, setPopupStyle] = useState({});
319
+ const [popupStyle, setPopupStyle] = useState({
320
+ position: "fixed",
321
+ visibility: "hidden"
322
+ });
318
323
  const filteredOptions = useMemo(() => {
319
324
  const query = inputValue.trim().toLocaleLowerCase();
320
325
  if (!query || query === originalValue.toLocaleLowerCase()) return options;
@@ -332,7 +337,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
332
337
  bottom: window.innerHeight - rect.top + 4,
333
338
  left: rect.left,
334
339
  width: rect.width,
335
- maxHeight: Math.min(maxDropdownHeight, availableAbove)
340
+ maxHeight: Math.min(maxDropdownHeight, availableAbove),
341
+ visibility: popupReadyRef.current ? "visible" : "hidden"
336
342
  });
337
343
  return;
338
344
  }
@@ -341,7 +347,8 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
341
347
  top: rect.bottom + 4,
342
348
  left: rect.left,
343
349
  width: rect.width,
344
- maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow))
350
+ maxHeight: Math.min(maxDropdownHeight, Math.max(80, availableBelow)),
351
+ visibility: popupReadyRef.current ? "visible" : "hidden"
345
352
  });
346
353
  };
347
354
  positionPopup();
@@ -353,8 +360,22 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
353
360
  };
354
361
  }, [maxDropdownHeight]);
355
362
  useLayoutEffect(() => {
356
- activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
357
- }, [activeIndex, filteredOptions, popupStyle]);
363
+ if (popupReadyRef.current) return;
364
+ if (popupStyle.top === void 0 && popupStyle.bottom === void 0) return;
365
+ const selectedOption = popupRef.current?.querySelector('[aria-selected="true"]') ?? activeOptionRef.current;
366
+ selectedOption?.scrollIntoView?.({ block: "center", inline: "nearest" });
367
+ popupReadyRef.current = true;
368
+ setPopupStyle((current) => ({ ...current, visibility: "visible" }));
369
+ }, [popupStyle.bottom, popupStyle.top]);
370
+ useLayoutEffect(() => {
371
+ if (!popupReadyRef.current) return;
372
+ if (navigationSourceRef.current === "keyboard") {
373
+ activeOptionRef.current?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
374
+ } else if (navigationSourceRef.current === "filter" && popupRef.current) {
375
+ popupRef.current.scrollTop = 0;
376
+ }
377
+ navigationSourceRef.current = null;
378
+ }, [activeIndex, filteredOptions]);
358
379
  useEffect(() => {
359
380
  const handleOutsidePointer = (event) => {
360
381
  const target = event.target;
@@ -374,10 +395,12 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
374
395
  if (event.key === "Escape") onCancel();
375
396
  if (event.key === "ArrowDown") {
376
397
  event.preventDefault();
398
+ navigationSourceRef.current = "keyboard";
377
399
  setActiveIndex((index) => Math.min(filteredOptions.length - 1, index + 1));
378
400
  }
379
401
  if (event.key === "ArrowUp") {
380
402
  event.preventDefault();
403
+ navigationSourceRef.current = "keyboard";
381
404
  setActiveIndex((index) => Math.max(0, index - 1));
382
405
  }
383
406
  if (event.key === "Enter") {
@@ -400,6 +423,7 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
400
423
  onChange: (event) => {
401
424
  setInputValue(event.target.value);
402
425
  onChange(event.target.value);
426
+ navigationSourceRef.current = "filter";
403
427
  setActiveIndex(0);
404
428
  },
405
429
  onKeyDown: handleKeyDown
@@ -415,7 +439,10 @@ function SearchableChoiceEditor({ value, options, allowCustomValue, maxDropdownH
415
439
  "aria-selected": option.value === originalValue,
416
440
  className: `rvg-choice-item${index === activeIndex ? " is-active" : ""}${option.value === originalValue ? " is-selected" : ""}`,
417
441
  onPointerDown: (event) => event.preventDefault(),
418
- onMouseEnter: () => setActiveIndex(index),
442
+ onMouseEnter: () => {
443
+ navigationSourceRef.current = "pointer";
444
+ setActiveIndex(index);
445
+ },
419
446
  onClick: () => selectOption(option),
420
447
  children: [
421
448
  /* @__PURE__ */ jsx3("span", { children: option.label }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensui/lens-table",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "description": "A lightweight, canvas-rendered React table component for LensUI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",