useScrolling.mjs 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // Utilities
  2. import { shallowRef, watch } from 'vue';
  3. // Types
  4. export function useScrolling(listRef, textFieldRef) {
  5. const isScrolling = shallowRef(false);
  6. let scrollTimeout;
  7. function onListScroll(e) {
  8. cancelAnimationFrame(scrollTimeout);
  9. isScrolling.value = true;
  10. scrollTimeout = requestAnimationFrame(() => {
  11. scrollTimeout = requestAnimationFrame(() => {
  12. isScrolling.value = false;
  13. });
  14. });
  15. }
  16. async function finishScrolling() {
  17. await new Promise(resolve => requestAnimationFrame(resolve));
  18. await new Promise(resolve => requestAnimationFrame(resolve));
  19. await new Promise(resolve => requestAnimationFrame(resolve));
  20. await new Promise(resolve => {
  21. if (isScrolling.value) {
  22. const stop = watch(isScrolling, () => {
  23. stop();
  24. resolve();
  25. });
  26. } else resolve();
  27. });
  28. }
  29. async function onListKeydown(e) {
  30. if (e.key === 'Tab') {
  31. textFieldRef.value?.focus();
  32. }
  33. if (!['PageDown', 'PageUp', 'Home', 'End'].includes(e.key)) return;
  34. const el = listRef.value?.$el;
  35. if (!el) return;
  36. if (e.key === 'Home' || e.key === 'End') {
  37. el.scrollTo({
  38. top: e.key === 'Home' ? 0 : el.scrollHeight,
  39. behavior: 'smooth'
  40. });
  41. }
  42. await finishScrolling();
  43. const children = el.querySelectorAll(':scope > :not(.v-virtual-scroll__spacer)');
  44. if (e.key === 'PageDown' || e.key === 'Home') {
  45. const top = el.getBoundingClientRect().top;
  46. for (const child of children) {
  47. if (child.getBoundingClientRect().top >= top) {
  48. child.focus();
  49. break;
  50. }
  51. }
  52. } else {
  53. const bottom = el.getBoundingClientRect().bottom;
  54. for (const child of [...children].reverse()) {
  55. if (child.getBoundingClientRect().bottom <= bottom) {
  56. child.focus();
  57. break;
  58. }
  59. }
  60. }
  61. }
  62. return {
  63. onScrollPassive: onListScroll,
  64. onKeydown: onListKeydown
  65. }; // typescript doesn't know about vue's event merging
  66. }
  67. //# sourceMappingURL=useScrolling.mjs.map