feat: add list scroll

This commit is contained in:
Curry Yang
2024-11-18 18:10:14 +08:00
committed by Curry Yang
parent cda2967f21
commit a31c19589f
2 changed files with 88 additions and 16 deletions

View File

@@ -1,3 +1,4 @@
import _ from 'lodash';
import React, { useCallback, useEffect, useState } from 'react';
import { Button, GridList, Menu, MenuItem, MenuTrigger, Popover, type Selection } from 'react-aria-components';
import { useFetcher, useNavigate } from 'react-router-dom';
@@ -8,6 +9,7 @@ import type { MockRoute } from '../../../models/mock-route';
import type { Request } from '../../../models/request';
import { INSOMNIA_TAB_HEIGHT } from '../../constant';
import { useInsomniaTabContext } from '../../context/app/insomnia-tab-context';
import { type Size, useResizeObserver } from '../../hooks/use-resize-observer';
import { Icon } from '../icon';
import { AddRequestToCollectionModal } from '../modals/add-request-to-collection-modal';
import { formatMethodName, getRequestMethodShortHand } from '../tags/method-tag';
@@ -38,6 +40,7 @@ export const OrganizationTabList = ({ showActiveStatus = true, currentPage = ''
const navigate = useNavigate();
const [showAddRequestModal, setShowAddRequestModal] = useState(false);
const [isOverFlow, setIsOverFlow] = useState(false);
const requestFetcher = useFetcher();
@@ -151,27 +154,69 @@ export const OrganizationTabList = ({ showActiveStatus = true, currentPage = ''
setShowAddRequestModal(true);
};
const tabListInnerRef = React.useRef<HTMLDivElement>(null);
const tabListWrapperRef = React.useRef<HTMLDivElement>(null);
const componentWrapperRef = React.useRef<HTMLDivElement>(null);
const onResize = () => {
console.log('resize');
const innerWidth = tabListInnerRef.current?.clientWidth;
const componentWrapperWidth = componentWrapperRef.current?.clientWidth;
if (innerWidth && componentWrapperWidth && innerWidth > componentWrapperWidth - 50) {
setIsOverFlow(true);
} else {
setIsOverFlow(false);
}
};
const debouncedOnResize = _.debounce<(size: Size) => void>(onResize, 500);
useResizeObserver(tabListWrapperRef, debouncedOnResize);
const scrollLeft = () => {
if (!tabListWrapperRef.current) {
return;
}
tabListWrapperRef.current.scrollLeft -= 150;
};
const scrollRight = () => {
if (!tabListWrapperRef.current) {
return;
}
tabListWrapperRef.current.scrollLeft += 150;
};
if (!tabList.length) {
return null;
};
return (
<div className="flex box-content border-b border-solid border-[--hl-sm]" style={{ height: `${INSOMNIA_TAB_HEIGHT}px` }}>
<GridList
aria-label="Insomnia Tabs"
onSelectionChange={handleSelectionChange}
selectedKeys={showActiveStatus && activeTabId ? [activeTabId] : []}
disallowEmptySelection
defaultSelectedKeys={['req_737492dce0c3460a8a55762e5d1bbd99']}
selectionMode="single"
selectionBehavior='replace'
className="flex bg-[--color-bg] max-w-[calc(100%-50px)] overflow-x-scroll hide-scrollbars h-[41px]"
// Use +1 height to mask the wrapper border, and let the custom element in InsomniaTab act as the fake border.we need different border for active tab
style={{ height: `${INSOMNIA_TAB_HEIGHT + 1}px` }}
items={tabList}
>
{item => <InsomniaTab tab={item} />}
</GridList>
<div className="flex box-content border-b border-solid border-[--hl-sm] bg-[--color-bg]" style={{ height: `${INSOMNIA_TAB_HEIGHT}px` }} ref={componentWrapperRef}>
<Button onPress={scrollLeft}>
<Icon icon="chevron-left" className={`w-[40px] cursor-pointer ${isOverFlow ? 'block' : 'hidden'}`} />
</Button>
<div className='max-w-[calc(100%-50px)] overflow-x-scroll hide-scrollbars scroll-smooth' ref={tabListWrapperRef}>
<GridList
aria-label="Insomnia Tabs"
onSelectionChange={handleSelectionChange}
selectedKeys={showActiveStatus && activeTabId ? [activeTabId] : []}
disallowEmptySelection
defaultSelectedKeys={['req_737492dce0c3460a8a55762e5d1bbd99']}
selectionMode="single"
selectionBehavior='replace'
className="flex h-[41px] w-fit"
// Use +1 height to mask the wrapper border, and let the custom element in InsomniaTab act as the fake border.we need different border for active tab
style={{ height: `${INSOMNIA_TAB_HEIGHT + 1}px` }}
items={tabList}
ref={tabListInnerRef}
>
{item => <InsomniaTab tab={item} />}
</GridList>
</div>
<Button onPress={scrollRight}>
<Icon icon="chevron-right" className={`w-[40px] cursor-pointer ${isOverFlow ? 'block' : 'hidden'}`} />
</Button>
<div className='flex items-center'>
<MenuTrigger>
<Button aria-label="Menu">

View File

@@ -0,0 +1,27 @@
import { useEffect, useRef } from 'react';
export interface Size {
width: number | undefined;
height: number | undefined;
}
export const useResizeObserver = (ref: React.RefObject<HTMLElement>, onResize: (size: Size) => void) => {
const onResizeRef = useRef<((size: Size) => void) | undefined>(undefined);
onResizeRef.current = onResize;
useEffect(() => {
if (!ref.current) {
return;
}
const observer = new ResizeObserver(([entry]) => {
const { width, height } = entry.contentRect;
onResizeRef.current?.({ width, height });
});
observer.observe(ref.current);
return () => {
observer.disconnect();
};
}, [ref]);
};