mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-21 06:37:36 -04:00
feat: scroll into view if needed
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Button, GridListItem } from 'react-aria-components';
|
||||
|
||||
import { scrollElementIntoView } from '../../../utils';
|
||||
import { useInsomniaTabContext } from '../../context/app/insomnia-tab-context';
|
||||
import { Icon } from '../icon';
|
||||
import { Tooltip } from '../tooltip';
|
||||
@@ -73,7 +74,7 @@ const WORKSPACE_TAB_UI_MAP: Record<string, any> = {
|
||||
|
||||
export const InsomniaTab = ({ tab }: { tab: BaseTab }) => {
|
||||
|
||||
const { closeTabById } = useInsomniaTabContext();
|
||||
const { closeTabById, currentOrgTabs } = useInsomniaTabContext();
|
||||
|
||||
const renderTabIcon = (type: TabEnum) => {
|
||||
if (WORKSPACE_TAB_UI_MAP[type]) {
|
||||
@@ -126,11 +127,18 @@ export const InsomniaTab = ({ tab }: { tab: BaseTab }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const scrollIntoView = useCallback((node: HTMLDivElement) => {
|
||||
if (node && currentOrgTabs.activeTabId === tab.id) {
|
||||
scrollElementIntoView(node, { behavior: 'instant' });
|
||||
}
|
||||
}, [currentOrgTabs.activeTabId, tab.id]);
|
||||
|
||||
return (
|
||||
<GridListItem
|
||||
textValue='tab'
|
||||
id={tab.id}
|
||||
className="outline-none aria-selected:text-[--color-font] aria-selected:bg-[--hl-sm] hover:bg-[--hl-xs]"
|
||||
ref={scrollIntoView}
|
||||
>
|
||||
{({ isSelected, isHovered }) => (
|
||||
<Tooltip delay={1000} message={`${tab.projectName} / ${tab.workspaceName}`} className='h-full'>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import _, { set } 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';
|
||||
@@ -188,14 +188,18 @@ export const OrganizationTabList = ({ showActiveStatus = true, currentPage = ''
|
||||
if (!tabListWrapperRef.current) {
|
||||
return;
|
||||
}
|
||||
tabListWrapperRef.current.style.scrollBehavior = 'smooth';
|
||||
tabListWrapperRef.current.scrollLeft -= 150;
|
||||
tabListWrapperRef.current.style.scrollBehavior = 'auto';
|
||||
};
|
||||
|
||||
const scrollRight = () => {
|
||||
if (!tabListWrapperRef.current) {
|
||||
return;
|
||||
}
|
||||
tabListWrapperRef.current.style.scrollBehavior = 'smooth';
|
||||
tabListWrapperRef.current.scrollLeft += 150;
|
||||
tabListWrapperRef.current.style.scrollBehavior = 'auto';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -254,7 +258,7 @@ export const OrganizationTabList = ({ showActiveStatus = true, currentPage = ''
|
||||
<Button onPress={scrollLeft} isDisabled={leftScrollDisable} className={`${leftScrollDisable && 'cursor-not-allowed'}`}>
|
||||
<Icon icon="chevron-left" className={`w-[30px] ${isOverFlow ? 'block' : 'hidden'}`} />
|
||||
</Button>
|
||||
<div className='max-w-[calc(100%-40px)] overflow-x-scroll hide-scrollbars scroll-smooth' ref={tabListWrapperRef} onScroll={handleScroll}>
|
||||
<div className='max-w-[calc(100%-40px)] overflow-x-scroll hide-scrollbars' ref={tabListWrapperRef} onScroll={handleScroll}>
|
||||
<GridList
|
||||
aria-label="Insomnia Tabs"
|
||||
onSelectionChange={handleSelectionChange}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { IconName } from '@fortawesome/fontawesome-svg-core';
|
||||
import type { ServiceError, StatusObject } from '@grpc/grpc-js';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import React, { type FC, Fragment, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import React, { type FC, Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Breadcrumb,
|
||||
Breadcrumbs,
|
||||
@@ -69,6 +69,7 @@ import {
|
||||
type WebSocketRequest,
|
||||
} from '../../models/websocket-request';
|
||||
import { isDesign, isScratchpad } from '../../models/workspace';
|
||||
import { scrollElementIntoView } from '../../utils';
|
||||
import { getGrpcConnectionErrorDetails, isGrpcConnectionError } from '../../utils/grpc';
|
||||
import { invariant } from '../../utils/invariant';
|
||||
import { DropdownHint } from '../components/base/dropdown/dropdown-hint';
|
||||
@@ -1271,6 +1272,12 @@ const CollectionGridListItem = ({
|
||||
|
||||
const isSelected = item.doc._id === params.requestId || item.doc._id === params.requestGroupId;
|
||||
|
||||
const scrollIntoView = useCallback((node: HTMLDivElement) => {
|
||||
if (isSelected && node) {
|
||||
scrollElementIntoView(node, { behavior: 'instant' });
|
||||
}
|
||||
}, [isSelected]);
|
||||
|
||||
return (
|
||||
<GridListItem
|
||||
id={item.doc._id}
|
||||
@@ -1280,6 +1287,7 @@ const CollectionGridListItem = ({
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
ref={scrollIntoView}
|
||||
onContextMenu={e => {
|
||||
e.preventDefault();
|
||||
setIsContextMenuOpen(true);
|
||||
|
||||
6
packages/insomnia/src/utils/index.ts
Normal file
6
packages/insomnia/src/utils/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const scrollElementIntoView = (element: HTMLElement, options?: ScrollIntoViewOptions) => {
|
||||
if (element) {
|
||||
// @ts-expect-error -- scrollIntoViewIfNeeded is not a standard method
|
||||
element.scrollIntoViewIfNeeded ? element.scrollIntoViewIfNeeded() : element.scrollIntoView(options);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user