mirror of
https://github.com/Kong/insomnia.git
synced 2026-10-12 11:44:45 -04:00
fix: Create workspace below the focus item when created in sidebar (#10370)
* fix sidebar shortcut issues * fix sort function
This commit is contained in:
1 parent
6469a6eaeb
commit
cb139fff6e
5 files changed
+247
-211
No files matched your search
+11
-1
@@ -26,9 +26,17 @@ interface Props {
|
||||
isOpen: boolean;
|
||||
onOpenChange: (isOpen: boolean) => void;
|
||||
triggerRef: RefObject<HTMLElement | null>;
|
||||
onWorkspaceCreated: (workspaceId: string) => void;
|
||||
}
|
||||
|
||||
export const SidebarShortcutActionsDropdown = ({ target, storageRules, isOpen, onOpenChange, triggerRef }: Props) => {
|
||||
export const SidebarShortcutActionsDropdown = ({
|
||||
target,
|
||||
storageRules,
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
triggerRef,
|
||||
onWorkspaceCreated,
|
||||
}: Props) => {
|
||||
const [newWorkspaceModalState, setNewWorkspaceModalState] = useState<{
|
||||
scope: 'collection' | 'design' | 'mock-server' | 'environment' | 'mcp';
|
||||
isOpen: boolean;
|
||||
@@ -166,6 +174,8 @@ export const SidebarShortcutActionsDropdown = ({ target, storageRules, isOpen, o
|
||||
project={target.kind === 'project' ? target.doc : target.project}
|
||||
storageRules={storageRules}
|
||||
scope={newWorkspaceModalState.scope}
|
||||
redirectAfterCreate={false}
|
||||
onCreateWorkspace={onWorkspaceCreated}
|
||||
onOpenChange={nextIsOpen => {
|
||||
setNewWorkspaceModalState(prev =>
|
||||
prev
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
export { NewProjectButton } from './new-project-button';
|
||||
export { SidebarSearchField } from './sidebar-search-field';
|
||||
export { SideBarTabList } from './sidebar-tab-list';
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import { Button as BasicButton } from '~/basic-components/button';
|
||||
|
||||
import { Icon } from '../../../icon';
|
||||
|
||||
export const NewProjectButton = ({ onPress, isDisabled }: { onPress: () => void; isDisabled?: boolean }) => (
|
||||
<BasicButton
|
||||
aria-label="Create new Project"
|
||||
onPress={onPress}
|
||||
isDisabled={isDisabled}
|
||||
className="flex h-full items-center justify-center gap-1 rounded-xs px-2 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
|
||||
>
|
||||
<Icon icon="plus" className="h-2.5 w-2.5" />
|
||||
<span>New Project</span>
|
||||
</BasicButton>
|
||||
);
|
||||
+217
-210
@@ -16,7 +16,7 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Button, GridList, GridListItem, type Selection, Tabs, Tooltip, TooltipTrigger } from 'react-aria-components';
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router';
|
||||
import { href, useNavigate, useParams, useSearchParams } from 'react-router';
|
||||
import * as reactUse from 'react-use';
|
||||
|
||||
import { Button as BasicButton } from '~/basic-components/button';
|
||||
@@ -33,14 +33,17 @@ import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import type { WorkspaceSortOrder } from '~/ui/components/dropdowns/sidebar-project-dropdown';
|
||||
import { SidebarShortcutActionsDropdown } from '~/ui/components/dropdowns/sidebar-shortcut-actions-dropdown';
|
||||
import { SidebarWorkspaceDropdown } from '~/ui/components/dropdowns/sidebar-workspace-dropdown';
|
||||
import { useDocBodyKeyboardShortcuts } from '~/ui/components/keydown-binder';
|
||||
import { useKeyboardShortcuts } from '~/ui/components/keydown-binder';
|
||||
import { KongLogo } from '~/ui/components/kong-logo';
|
||||
import { showModal } from '~/ui/components/modals';
|
||||
import { AlertModal } from '~/ui/components/modals/alert-modal';
|
||||
import { AskModal } from '~/ui/components/modals/ask-modal';
|
||||
import { KonnectSettingsModal } from '~/ui/components/modals/konnect-settings-modal';
|
||||
import { SidebarSearchField } from '~/ui/components/sidebar/project-navigation-sidebar/components/sidebar-search-field';
|
||||
import { SideBarTabList } from '~/ui/components/sidebar/project-navigation-sidebar/components/sidebar-tab-list';
|
||||
import {
|
||||
NewProjectButton,
|
||||
SidebarSearchField,
|
||||
SideBarTabList,
|
||||
} from '~/ui/components/sidebar/project-navigation-sidebar/components';
|
||||
import { EmptyNode } from '~/ui/components/sidebar/project-navigation-sidebar/empty-node';
|
||||
import { KonnectEnvOnboarding } from '~/ui/components/sidebar/project-navigation-sidebar/konnect-env-onboarding';
|
||||
import { KonnectSyncIntro } from '~/ui/components/sidebar/project-navigation-sidebar/konnect-sync-intro/konnect-sync-intro';
|
||||
@@ -101,17 +104,11 @@ function getRelativeTimeString(timestamp: number, now: number = Date.now()): str
|
||||
return `${days}d ${hours % 24}h ago`;
|
||||
}
|
||||
|
||||
const NewProjectButton = ({ onPress, isDisabled }: { onPress: () => void; isDisabled?: boolean }) => (
|
||||
<BasicButton
|
||||
aria-label="Create new Project"
|
||||
onPress={onPress}
|
||||
isDisabled={isDisabled}
|
||||
className="flex h-full items-center justify-center gap-1 rounded-xs px-2 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
|
||||
>
|
||||
<Icon icon="plus" className="h-2.5 w-2.5" />
|
||||
<span>New Project</span>
|
||||
</BasicButton>
|
||||
);
|
||||
const workspaceManualSortMethod = (a: Workspace, b: Workspace, localOrder: string[]) => {
|
||||
const aIndex = localOrder.indexOf(a._id);
|
||||
const bIndex = localOrder.indexOf(b._id);
|
||||
return (aIndex === -1 ? Infinity : aIndex) - (bIndex === -1 ? Infinity : bIndex);
|
||||
};
|
||||
|
||||
const ProjectNavigationSidebarInner = (
|
||||
{ storageRules, konnectSyncEnabled, onCreateProject, activeTab, setActiveTab }: ProjectNavigationSidebarProps,
|
||||
@@ -431,16 +428,9 @@ const ProjectNavigationSidebarInner = (
|
||||
let sortedWorkspaces: Workspace[] = [];
|
||||
if (workspaceOrder === 'type-manual') {
|
||||
const localOrder = localWorkspaceOrders?.[projectId];
|
||||
if (localOrder) {
|
||||
const orderIndexByWorkspaceId = new Map(localOrder.map((workspaceId, index) => [workspaceId, index]));
|
||||
sortedWorkspaces = [...workspaces].sort((a, b) => {
|
||||
const ai = orderIndexByWorkspaceId.get(a._id) ?? Infinity;
|
||||
const bi = orderIndexByWorkspaceId.get(b._id) ?? Infinity;
|
||||
return ai - bi;
|
||||
});
|
||||
} else {
|
||||
sortedWorkspaces = [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b));
|
||||
}
|
||||
sortedWorkspaces = localOrder
|
||||
? [...workspaces].sort((a, b) => workspaceManualSortMethod(a, b, localOrder))
|
||||
: [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b));
|
||||
} else {
|
||||
sortedWorkspaces = [...workspaces].sort((a, b) => sortMethodMap[workspaceOrder](a, b));
|
||||
}
|
||||
@@ -712,11 +702,13 @@ const ProjectNavigationSidebarInner = (
|
||||
const workspaces = organizationWorkspaces.filter(w => w.parentId === targetProjectId);
|
||||
const currentWorkspaceSortOrder = projectWorkspaceSortOrder[targetProjectId] || 'type-manual';
|
||||
// Get the base order of workspace before re-order
|
||||
const localOrder = localWorkspaceOrders?.[targetProjectId];
|
||||
const baseOrder =
|
||||
// if current order is manual, use the current order in local state or default sort by created time; otherwise use current order to sort
|
||||
currentWorkspaceSortOrder === 'type-manual'
|
||||
? localWorkspaceOrders?.[targetProjectId] ||
|
||||
[...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b)).map(w => w._id)
|
||||
? localOrder
|
||||
? [...workspaces].sort((a, b) => workspaceManualSortMethod(a, b, localOrder)).map(w => w._id)
|
||||
: [...workspaces].sort((a, b) => sortMethodMap['created-asc'](a, b)).map(w => w._id)
|
||||
: [...workspaces].sort((a, b) => sortMethodMap[currentWorkspaceSortOrder](a, b)).map(w => w._id);
|
||||
const reordered = (baseOrder as string[]).filter((id: string) => id !== draggedId);
|
||||
|
||||
@@ -997,7 +989,7 @@ const ProjectNavigationSidebarInner = (
|
||||
}
|
||||
}, [selectedItemId]);
|
||||
|
||||
useDocBodyKeyboardShortcuts({
|
||||
useKeyboardShortcuts(() => parentRef.current!, {
|
||||
sidebar_showCreateDropdown: event => {
|
||||
if (!isProjectTabActive) {
|
||||
return;
|
||||
@@ -1204,32 +1196,32 @@ const ProjectNavigationSidebarInner = (
|
||||
) : (
|
||||
<div className="flex items-center gap-1">
|
||||
{syncing ? (
|
||||
<Button
|
||||
aria-label="Cancel sync"
|
||||
onPress={cancelSync}
|
||||
className="flex h-full items-center justify-center gap-1 rounded-xs border border-solid border-(--hl-sm) px-2 text-sm text-(--color-font) transition-all hover:bg-(--hl-xs) focus:outline-none"
|
||||
>
|
||||
Cancel
|
||||
<Icon icon="stop-circle" />
|
||||
</Button>
|
||||
) : (
|
||||
<TooltipTrigger delay={300}>
|
||||
<Button
|
||||
aria-label="Sync Konnect"
|
||||
onPress={handleSync}
|
||||
aria-label="Cancel sync"
|
||||
onPress={cancelSync}
|
||||
className="flex h-full items-center justify-center gap-1 rounded-xs border border-solid border-(--hl-sm) px-2 text-sm text-(--color-font) transition-all hover:bg-(--hl-xs) focus:outline-none"
|
||||
>
|
||||
<Icon icon="refresh" />
|
||||
Sync
|
||||
Cancel
|
||||
<Icon icon="stop-circle" />
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
className="rounded-md border border-solid border-(--hl-sm) bg-(--color-bg) px-3 py-1.5 text-xs text-(--color-font) shadow-lg select-none"
|
||||
>
|
||||
<LastSyncedLabel lastSyncedAt={lastSyncedAt ?? null} />
|
||||
</Tooltip>
|
||||
</TooltipTrigger>
|
||||
)}
|
||||
) : (
|
||||
<TooltipTrigger delay={300}>
|
||||
<Button
|
||||
aria-label="Sync Konnect"
|
||||
onPress={handleSync}
|
||||
className="flex h-full items-center justify-center gap-1 rounded-xs border border-solid border-(--hl-sm) px-2 text-sm text-(--color-font) transition-all hover:bg-(--hl-xs) focus:outline-none"
|
||||
>
|
||||
<Icon icon="refresh" />
|
||||
Sync
|
||||
</Button>
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
className="rounded-md border border-solid border-(--hl-sm) bg-(--color-bg) px-3 py-1.5 text-xs text-(--color-font) shadow-lg select-none"
|
||||
>
|
||||
<LastSyncedLabel lastSyncedAt={lastSyncedAt ?? null} />
|
||||
</Tooltip>
|
||||
</TooltipTrigger>
|
||||
)}
|
||||
<Button
|
||||
aria-label="Konnect settings"
|
||||
onPress={() => setShowKonnectConfigModal(true)}
|
||||
@@ -1296,65 +1288,78 @@ const ProjectNavigationSidebarInner = (
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<GridList
|
||||
aria-label="Project Navigation Tree"
|
||||
items={virtualizer.getVirtualItems()}
|
||||
style={{ height: virtualizer.getTotalSize(), position: 'relative' }}
|
||||
className="outline-hidden"
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
selectionMode="single"
|
||||
disabledKeys={pinnedHeaderKeys}
|
||||
disabledBehavior="all"
|
||||
dragAndDropHooks={sidebarDragAndDropHooks}
|
||||
>
|
||||
{virtualItem => {
|
||||
const item = visibleFlatItems[virtualItem.index];
|
||||
if (!item) return null;
|
||||
<GridList
|
||||
aria-label="Project Navigation Tree"
|
||||
items={virtualizer.getVirtualItems()}
|
||||
style={{ height: virtualizer.getTotalSize(), position: 'relative' }}
|
||||
className="outline-hidden"
|
||||
selectedKeys={selectedKeys}
|
||||
onSelectionChange={setSelectedKeys}
|
||||
selectionMode="single"
|
||||
disabledKeys={pinnedHeaderKeys}
|
||||
disabledBehavior="all"
|
||||
dragAndDropHooks={sidebarDragAndDropHooks}
|
||||
>
|
||||
{virtualItem => {
|
||||
const item = visibleFlatItems[virtualItem.index];
|
||||
if (!item) return null;
|
||||
|
||||
// Keep the focus ring on the targeted item while the create dropdown is open,
|
||||
// since keyboard focus has moved into the dropdown menu.
|
||||
const isShortcutTarget =
|
||||
isShortcutCreateOpen && shortcutTargetItemId === item.doc._id && item.kind !== 'pinnedRequest';
|
||||
// Keep the focus ring on the targeted item while the create dropdown is open,
|
||||
// since keyboard focus has moved into the dropdown menu.
|
||||
const isShortcutTarget =
|
||||
isShortcutCreateOpen && shortcutTargetItemId === item.doc._id && item.kind !== 'pinnedRequest';
|
||||
|
||||
return (
|
||||
<GridListItem
|
||||
key={getSidebarGridListItemId(item)}
|
||||
id={getSidebarGridListItemId(item)}
|
||||
textValue={item.doc.name || item.kind}
|
||||
onAuxClick={e => {
|
||||
if (e.button === 1 && item.kind === 'collectionChild') {
|
||||
e.preventDefault();
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
project: item.project,
|
||||
workspace: item.workspace,
|
||||
item: item.doc,
|
||||
},
|
||||
{ withTab: true, shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
}}
|
||||
onPress={async e => {
|
||||
const docId = item.doc._id;
|
||||
if (item.kind === 'project') {
|
||||
if (routeInfo?.resourceId === docId) {
|
||||
toggleProjectOrWorkspace(docId);
|
||||
} else {
|
||||
!isScratchPad &&
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.projectSwitched,
|
||||
properties: { project_id: docId },
|
||||
});
|
||||
!isScratchPad && navigate(`/organization/${organizationId}/project/${docId}`);
|
||||
return (
|
||||
<GridListItem
|
||||
key={getSidebarGridListItemId(item)}
|
||||
id={getSidebarGridListItemId(item)}
|
||||
textValue={item.doc.name || item.kind}
|
||||
onAuxClick={e => {
|
||||
if (e.button === 1 && item.kind === 'collectionChild') {
|
||||
e.preventDefault();
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
project: item.project,
|
||||
workspace: item.workspace,
|
||||
item: item.doc,
|
||||
},
|
||||
{ withTab: true, shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
} else if (item.kind === 'workspace') {
|
||||
if (item.doc.scope === 'collection' && enableCollectionFocus) {
|
||||
// Narrow the sidebar into this collection's tree, and open it
|
||||
// in the main pane if it isn't already active.
|
||||
focusWorkspace(docId, item.project._id);
|
||||
if (routeInfo?.resourceId !== docId || routeInfo?.routeId === 'runner') {
|
||||
}}
|
||||
onPress={async e => {
|
||||
const docId = item.doc._id;
|
||||
if (item.kind === 'project') {
|
||||
if (routeInfo?.resourceId === docId) {
|
||||
toggleProjectOrWorkspace(docId);
|
||||
} else {
|
||||
!isScratchPad &&
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.projectSwitched,
|
||||
properties: { project_id: docId },
|
||||
});
|
||||
!isScratchPad && navigate(`/organization/${organizationId}/project/${docId}`);
|
||||
}
|
||||
} else if (item.kind === 'workspace') {
|
||||
if (item.doc.scope === 'collection' && enableCollectionFocus) {
|
||||
// Narrow the sidebar into this collection's tree, and open it
|
||||
// in the main pane if it isn't already active.
|
||||
focusWorkspace(docId, item.project._id);
|
||||
if (routeInfo?.resourceId !== docId || routeInfo?.routeId === 'runner') {
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
project: item.project,
|
||||
workspace: item.doc,
|
||||
item: item.doc,
|
||||
},
|
||||
{ withTab: isPrimaryClickModifier(e), shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
} else if (routeInfo?.resourceId === docId && routeInfo?.routeId !== 'runner') {
|
||||
toggleProjectOrWorkspace(docId);
|
||||
} else {
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
@@ -1365,116 +1370,105 @@ const ProjectNavigationSidebarInner = (
|
||||
{ withTab: isPrimaryClickModifier(e), shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
} else if (routeInfo?.resourceId === docId && routeInfo?.routeId !== 'runner') {
|
||||
toggleProjectOrWorkspace(docId);
|
||||
} else {
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
project: item.project,
|
||||
workspace: item.doc,
|
||||
item: item.doc,
|
||||
},
|
||||
{ withTab: isPrimaryClickModifier(e), shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
// Dismiss onboarding when user navigates to the highlighted environment
|
||||
if (docId === onboardingEnvWorkspaceId) {
|
||||
dismissEnvOnboarding();
|
||||
}
|
||||
} else if (item.kind === 'collectionChild' || item.kind === 'pinnedRequest') {
|
||||
// Clicking anything inside a collection (folder, request, etc.) focuses it.
|
||||
// Guard so re-clicking within an already-focused collection doesn't replay the transition.
|
||||
if (enableCollectionFocus && focusedWorkspaceId !== item.workspace._id) {
|
||||
focusWorkspace(item.workspace._id, item.project._id);
|
||||
}
|
||||
if (
|
||||
routeInfo?.resourceId === docId &&
|
||||
models.requestGroup.isRequestGroupId(docId) &&
|
||||
routeInfo?.routeId !== 'runner'
|
||||
) {
|
||||
toggleRequestGroups([docId], item.workspace);
|
||||
} else {
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
project: item.project,
|
||||
workspace: item.workspace,
|
||||
item: item.doc,
|
||||
},
|
||||
{ withTab: isPrimaryClickModifier(e), shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
className={`group rounded-xs outline-hidden select-none data-focus-visible:z-10 data-focus-visible:ring-2 data-focus-visible:ring-(--color-surprise) data-focus-visible:ring-inset ${isShortcutTarget ? 'z-10 ring-2 ring-(--color-surprise) ring-inset' : ''}`}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${virtualItem.size}px`,
|
||||
transform: `translateY(${virtualItem.start}px)`,
|
||||
}}
|
||||
>
|
||||
{item.kind === 'project' && (
|
||||
<ProjectNode
|
||||
item={item}
|
||||
onToggle={toggleProjectOrWorkspace}
|
||||
storageRules={storageRules}
|
||||
sortOrder={projectWorkspaceSortOrder[item.doc._id] || 'type-manual'}
|
||||
onSortOrderChange={newSortOrder =>
|
||||
setProjectWorkspaceSortOrder(prev => {
|
||||
const newProjectWorkspaceSortOrder = { ...prev, [item.doc._id]: newSortOrder };
|
||||
return newProjectWorkspaceSortOrder;
|
||||
})
|
||||
}
|
||||
onDeleteProject={projectId =>
|
||||
deleteProjectFetcher.submit({
|
||||
organizationId,
|
||||
projectId,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{item.kind === 'workspace' && (
|
||||
<WorkspaceNode
|
||||
item={item}
|
||||
onToggle={toggleProjectOrWorkspace}
|
||||
sortOrder={collectionSortOrders[item.doc._id] || 'type-manual'}
|
||||
onSortOrderChange={newSortOder => {
|
||||
if (item.doc.scope === 'collection') {
|
||||
setCollectionSortOrders(prev => {
|
||||
const newCollectionSortOrders = { ...prev, [item.doc._id]: newSortOder };
|
||||
return newCollectionSortOrders;
|
||||
});
|
||||
// Dismiss onboarding when user navigates to the highlighted environment
|
||||
if (docId === onboardingEnvWorkspaceId) {
|
||||
dismissEnvOnboarding();
|
||||
}
|
||||
}}
|
||||
highlighted={item.doc._id === onboardingEnvWorkspaceId}
|
||||
nodeRef={item.doc._id === onboardingEnvWorkspaceId ? setEnvOnboardingNode : undefined}
|
||||
/>
|
||||
)}
|
||||
} else if (item.kind === 'collectionChild' || item.kind === 'pinnedRequest') {
|
||||
// Clicking anything inside a collection (folder, request, etc.) focuses it.
|
||||
// Guard so re-clicking within an already-focused collection doesn't replay the transition.
|
||||
if (enableCollectionFocus && focusedWorkspaceId !== item.workspace._id) {
|
||||
focusWorkspace(item.workspace._id, item.project._id);
|
||||
}
|
||||
if (
|
||||
routeInfo?.resourceId === docId &&
|
||||
models.requestGroup.isRequestGroupId(docId) &&
|
||||
routeInfo?.routeId !== 'runner'
|
||||
) {
|
||||
toggleRequestGroups([docId], item.workspace);
|
||||
} else {
|
||||
tabNavigate(
|
||||
{
|
||||
organization: organizationId,
|
||||
project: item.project,
|
||||
workspace: item.workspace,
|
||||
item: item.doc,
|
||||
},
|
||||
{ withTab: isPrimaryClickModifier(e), shouldNavigate: true, searchParams },
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
className={`group rounded-xs outline-hidden select-none data-focus-visible:z-10 data-focus-visible:ring-2 data-focus-visible:ring-(--color-surprise) data-focus-visible:ring-inset ${isShortcutTarget ? 'z-10 ring-2 ring-(--color-surprise) ring-inset' : ''}`}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: `${virtualItem.size}px`,
|
||||
transform: `translateY(${virtualItem.start}px)`,
|
||||
}}
|
||||
>
|
||||
{item.kind === 'project' && (
|
||||
<ProjectNode
|
||||
item={item}
|
||||
onToggle={toggleProjectOrWorkspace}
|
||||
storageRules={storageRules}
|
||||
sortOrder={projectWorkspaceSortOrder[item.doc._id] || 'type-manual'}
|
||||
onSortOrderChange={newSortOrder =>
|
||||
setProjectWorkspaceSortOrder(prev => {
|
||||
const newProjectWorkspaceSortOrder = { ...prev, [item.doc._id]: newSortOrder };
|
||||
return newProjectWorkspaceSortOrder;
|
||||
})
|
||||
}
|
||||
onDeleteProject={projectId =>
|
||||
deleteProjectFetcher.submit({
|
||||
organizationId,
|
||||
projectId,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{item.kind === 'pinnedHeader' && <PinnedHeaderNode depthOffset={treeDepthOffset} />}
|
||||
{item.kind === 'workspace' && (
|
||||
<WorkspaceNode
|
||||
item={item}
|
||||
onToggle={toggleProjectOrWorkspace}
|
||||
sortOrder={collectionSortOrders[item.doc._id] || 'type-manual'}
|
||||
onSortOrderChange={newSortOder => {
|
||||
if (item.doc.scope === 'collection') {
|
||||
setCollectionSortOrders(prev => {
|
||||
const newCollectionSortOrders = { ...prev, [item.doc._id]: newSortOder };
|
||||
return newCollectionSortOrders;
|
||||
});
|
||||
}
|
||||
}}
|
||||
highlighted={item.doc._id === onboardingEnvWorkspaceId}
|
||||
nodeRef={item.doc._id === onboardingEnvWorkspaceId ? setEnvOnboardingNode : undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
{item.kind === 'collectionChild' && (
|
||||
<RequestNode item={item} onToggleFolder={toggleRequestGroups} depthOffset={treeDepthOffset} />
|
||||
)}
|
||||
{item.kind === 'pinnedHeader' && <PinnedHeaderNode depthOffset={treeDepthOffset} />}
|
||||
|
||||
{item.kind === 'pinnedRequest' && (
|
||||
<RequestNode item={item} onToggleFolder={toggleRequestGroups} depthOffset={treeDepthOffset} />
|
||||
)}
|
||||
{item.kind === 'collectionChild' && (
|
||||
<RequestNode item={item} onToggleFolder={toggleRequestGroups} depthOffset={treeDepthOffset} />
|
||||
)}
|
||||
|
||||
{item.kind === 'unsyncedWorkspace' && <UnsyncedWorkspaceNode item={item} />}
|
||||
{item.kind === 'pinnedRequest' && (
|
||||
<RequestNode item={item} onToggleFolder={toggleRequestGroups} depthOffset={treeDepthOffset} />
|
||||
)}
|
||||
|
||||
{item.kind === 'emptyProject' || item.kind === 'emptyCollection' || item.kind === 'emptyFolder' ? (
|
||||
<EmptyNode item={item} storageRules={storageRules} depthOffset={treeDepthOffset} />
|
||||
) : null}
|
||||
</GridListItem>
|
||||
);
|
||||
}}
|
||||
</GridList>
|
||||
{item.kind === 'unsyncedWorkspace' && <UnsyncedWorkspaceNode item={item} />}
|
||||
|
||||
{item.kind === 'emptyProject' ||
|
||||
item.kind === 'emptyCollection' ||
|
||||
item.kind === 'emptyFolder' ? (
|
||||
<EmptyNode item={item} storageRules={storageRules} depthOffset={treeDepthOffset} />
|
||||
) : null}
|
||||
</GridListItem>
|
||||
);
|
||||
}}
|
||||
</GridList>
|
||||
</div>
|
||||
</div>
|
||||
{shortcutTargetItem && (
|
||||
@@ -1484,6 +1478,19 @@ const ProjectNavigationSidebarInner = (
|
||||
isOpen={isShortcutCreateOpen}
|
||||
onOpenChange={setIsShortcutCreateOpen}
|
||||
triggerRef={shortcutCreateTriggerRef}
|
||||
onWorkspaceCreated={newWorkspaceId => {
|
||||
const projectId =
|
||||
shortcutTargetItem.kind === 'workspace' ? shortcutTargetItem.project._id : shortcutTargetItem.doc._id;
|
||||
const targetWorkspaceId = shortcutTargetItem.kind === 'workspace' ? shortcutTargetItem.doc._id : null;
|
||||
handleLocalWorkspaceReorder(projectId, projectId, newWorkspaceId, targetWorkspaceId, 'after');
|
||||
return navigate(
|
||||
href(`/organization/:organizationId/project/:projectId/workspace/:workspaceId`, {
|
||||
organizationId,
|
||||
projectId,
|
||||
workspaceId: newWorkspaceId,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const createAppDataQueryClient = () =>
|
||||
staleTime: Infinity,
|
||||
gcTime: Infinity,
|
||||
retry: false,
|
||||
// Local database queries should always be fetched regardless of network connectivity.
|
||||
networkMode: 'always',
|
||||
},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user