refactor: streamline git sidebar header
Move action tabs into the Git header with sync beside them Merge worktree actions into the Update tab Place stash access next to changed files
This commit is contained in:
@@ -42,6 +42,7 @@ type SortableTabsStripProps = {
|
|||||||
activePillInsetClassName?: string;
|
activePillInsetClassName?: string;
|
||||||
activePillButtonClassName?: string;
|
activePillButtonClassName?: string;
|
||||||
inactiveTabsIconOnly?: boolean;
|
inactiveTabsIconOnly?: boolean;
|
||||||
|
iconOnlyActiveTab?: boolean;
|
||||||
animateActivePill?: boolean;
|
animateActivePill?: boolean;
|
||||||
activePillLowercase?: boolean;
|
activePillLowercase?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -94,6 +95,7 @@ export const SortableTabsStrip: React.FC<SortableTabsStripProps> = ({
|
|||||||
activePillInsetClassName,
|
activePillInsetClassName,
|
||||||
activePillButtonClassName,
|
activePillButtonClassName,
|
||||||
inactiveTabsIconOnly = false,
|
inactiveTabsIconOnly = false,
|
||||||
|
iconOnlyActiveTab = false,
|
||||||
animateActivePill,
|
animateActivePill,
|
||||||
activePillLowercase = true,
|
activePillLowercase = true,
|
||||||
className,
|
className,
|
||||||
@@ -367,6 +369,7 @@ export const SortableTabsStrip: React.FC<SortableTabsStripProps> = ({
|
|||||||
const isActive = item.id === activeId;
|
const isActive = item.id === activeId;
|
||||||
const showInactiveIconOnly = inactiveTabsIconOnly && usesActivePillIndicator && !isActive && Boolean(item.icon);
|
const showInactiveIconOnly = inactiveTabsIconOnly && usesActivePillIndicator && !isActive && Boolean(item.icon);
|
||||||
const shouldShowLabel = !showInactiveIconOnly;
|
const shouldShowLabel = !showInactiveIconOnly;
|
||||||
|
const shouldShowIcon = Boolean(item.icon) && (!iconOnlyActiveTab || isActive);
|
||||||
const useIntrinsicActiveTab = inactiveTabsIconOnly && usesActivePillIndicator && isActive && !isScrollable && !useIntrinsicPillSizing;
|
const useIntrinsicActiveTab = inactiveTabsIconOnly && usesActivePillIndicator && isActive && !isScrollable && !useIntrinsicPillSizing;
|
||||||
const closable = item.closable !== false && Boolean(onClose);
|
const closable = item.closable !== false && Boolean(onClose);
|
||||||
const closeReplacesIcon = closable && Boolean(item.icon);
|
const closeReplacesIcon = closable && Boolean(item.icon);
|
||||||
@@ -436,7 +439,7 @@ export const SortableTabsStrip: React.FC<SortableTabsStripProps> = ({
|
|||||||
>
|
>
|
||||||
{usesActivePillIndicator ? (
|
{usesActivePillIndicator ? (
|
||||||
<>
|
<>
|
||||||
{item.icon ? (
|
{shouldShowIcon ? (
|
||||||
<span className="relative flex h-4 w-4 shrink-0 items-center justify-center">
|
<span className="relative flex h-4 w-4 shrink-0 items-center justify-center">
|
||||||
<span className={cn('flex items-center justify-center transition-opacity', closeReplacesIcon && (alwaysShowCloseControls ? 'opacity-0' : 'group-hover:opacity-0'))}>{item.icon}</span>
|
<span className={cn('flex items-center justify-center transition-opacity', closeReplacesIcon && (alwaysShowCloseControls ? 'opacity-0' : 'group-hover:opacity-0'))}>{item.icon}</span>
|
||||||
{closeReplacesIcon ? (
|
{closeReplacesIcon ? (
|
||||||
@@ -463,7 +466,7 @@ export const SortableTabsStrip: React.FC<SortableTabsStripProps> = ({
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className={cn('flex min-w-0 flex-nowrap items-center gap-1.5', !isScrollable && 'justify-center')}>
|
<span className={cn('flex min-w-0 flex-nowrap items-center gap-1.5', !isScrollable && 'justify-center')}>
|
||||||
{item.icon ? (
|
{shouldShowIcon ? (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative flex h-4 w-4 shrink-0 items-center justify-center transition-colors duration-200 ease-out',
|
'relative flex h-4 w-4 shrink-0 items-center justify-center transition-colors duration-200 ease-out',
|
||||||
|
|||||||
@@ -25,10 +25,8 @@ import {
|
|||||||
RiGitCommitLine,
|
RiGitCommitLine,
|
||||||
RiGitPullRequestLine,
|
RiGitPullRequestLine,
|
||||||
RiLoader4Line,
|
RiLoader4Line,
|
||||||
RiSplitCellsHorizontal,
|
|
||||||
} from '@remixicon/react';
|
} from '@remixicon/react';
|
||||||
import { toast } from '@/components/ui';
|
import { toast } from '@/components/ui';
|
||||||
import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip';
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -74,7 +72,7 @@ import { useI18n } from '@/lib/i18n';
|
|||||||
type SyncAction = 'fetch' | 'pull' | 'push' | 'sync' | null;
|
type SyncAction = 'fetch' | 'pull' | 'push' | 'sync' | null;
|
||||||
type CommitAction = 'commit' | 'commitAndPush' | null;
|
type CommitAction = 'commit' | 'commitAndPush' | null;
|
||||||
type BranchOperation = 'merge' | 'rebase' | null;
|
type BranchOperation = 'merge' | 'rebase' | null;
|
||||||
type ActionTab = 'commit' | 'branch' | 'pr' | 'worktree';
|
type ActionTab = 'commit' | 'branch' | 'pr';
|
||||||
type HistoryBranchDivider = {
|
type HistoryBranchDivider = {
|
||||||
insertBeforeIndex: number;
|
insertBeforeIndex: number;
|
||||||
branchName: string;
|
branchName: string;
|
||||||
@@ -84,7 +82,7 @@ type HistoryBranchDivider = {
|
|||||||
const GIT_ACTION_TAB_STORAGE_KEY = 'oc.git.actionTab';
|
const GIT_ACTION_TAB_STORAGE_KEY = 'oc.git.actionTab';
|
||||||
|
|
||||||
const isActionTab = (value: unknown): value is ActionTab =>
|
const isActionTab = (value: unknown): value is ActionTab =>
|
||||||
value === 'commit' || value === 'branch' || value === 'pr' || value === 'worktree';
|
value === 'commit' || value === 'branch' || value === 'pr';
|
||||||
|
|
||||||
|
|
||||||
type GitViewSnapshot = {
|
type GitViewSnapshot = {
|
||||||
@@ -526,13 +524,15 @@ export const GitView: React.FC = () => {
|
|||||||
{ id: 'commit', label: t('gitView.tabs.commit'), icon: <RiGitCommitLine className="h-3.5 w-3.5" /> },
|
{ id: 'commit', label: t('gitView.tabs.commit'), icon: <RiGitCommitLine className="h-3.5 w-3.5" /> },
|
||||||
{ id: 'branch', label: t('gitView.tabs.update'), icon: <RiGitMergeLine className="h-3.5 w-3.5" /> },
|
{ id: 'branch', label: t('gitView.tabs.update'), icon: <RiGitMergeLine className="h-3.5 w-3.5" /> },
|
||||||
{ id: 'pr', label: t('gitView.tabs.pr'), icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
{ id: 'pr', label: t('gitView.tabs.pr'), icon: <RiGitPullRequestLine className="h-3.5 w-3.5" /> },
|
||||||
{ id: 'worktree', label: t('gitView.tabs.worktree'), icon: <RiSplitCellsHorizontal className="h-3.5 w-3.5" /> },
|
|
||||||
], [t]);
|
], [t]);
|
||||||
const [actionTab, setActionTab] = React.useState<ActionTab>(() => {
|
const [actionTab, setActionTab] = React.useState<ActionTab>(() => {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return 'commit';
|
return 'commit';
|
||||||
}
|
}
|
||||||
const stored = window.localStorage.getItem(GIT_ACTION_TAB_STORAGE_KEY);
|
const stored = window.localStorage.getItem(GIT_ACTION_TAB_STORAGE_KEY);
|
||||||
|
if (stored === 'worktree') {
|
||||||
|
return 'branch';
|
||||||
|
}
|
||||||
return isActionTab(stored) ? stored : 'commit';
|
return isActionTab(stored) ? stored : 'commit';
|
||||||
});
|
});
|
||||||
const [remotes, setRemotes] = React.useState<GitRemote[]>([]);
|
const [remotes, setRemotes] = React.useState<GitRemote[]>([]);
|
||||||
@@ -2143,7 +2143,7 @@ export const GitView: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('flex h-full flex-col overflow-hidden', 'bg-sidebar')}>
|
<div className={cn('flex h-full flex-col overflow-hidden', 'bg-sidebar')}>
|
||||||
<GitHeader
|
<GitHeader
|
||||||
status={status}
|
status={status}
|
||||||
localBranches={localBranches}
|
localBranches={localBranches}
|
||||||
remoteBranches={remoteBranches}
|
remoteBranches={remoteBranches}
|
||||||
@@ -2161,10 +2161,12 @@ export const GitView: React.FC = () => {
|
|||||||
availableIdentities={availableIdentities}
|
availableIdentities={availableIdentities}
|
||||||
onSelectIdentity={handleApplyIdentity}
|
onSelectIdentity={handleApplyIdentity}
|
||||||
isApplyingIdentity={isSettingIdentity}
|
isApplyingIdentity={isSettingIdentity}
|
||||||
isWorktreeMode={!!worktreeMetadata}
|
isWorktreeMode={!!worktreeMetadata}
|
||||||
onOpenHistory={() => setIsHistoryDialogOpen(true)}
|
onOpenHistory={() => setIsHistoryDialogOpen(true)}
|
||||||
onOpenStashes={() => setIsStashesDialogOpen(true)}
|
actionTabItems={actionTabItems}
|
||||||
/>
|
activeActionTab={actionTab}
|
||||||
|
onSelectActionTab={(tabID) => setActionTab(tabID as ActionTab)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* In-progress operation banner */}
|
{/* In-progress operation banner */}
|
||||||
{currentDirectory && (
|
{currentDirectory && (
|
||||||
@@ -2185,18 +2187,6 @@ export const GitView: React.FC = () => {
|
|||||||
<div className="flex-1 min-h-0 overflow-hidden">
|
<div className="flex-1 min-h-0 overflow-hidden">
|
||||||
<div className="h-full min-h-0 flex flex-col">
|
<div className="h-full min-h-0 flex flex-col">
|
||||||
<div className={cn('min-w-0 min-h-0 h-full flex flex-col', 'bg-sidebar')}>
|
<div className={cn('min-w-0 min-h-0 h-full flex flex-col', 'bg-sidebar')}>
|
||||||
<div className={cn(isMobile ? 'h-10 px-1.5' : 'h-8 px-2')}>
|
|
||||||
<SortableTabsStrip
|
|
||||||
items={actionTabItems}
|
|
||||||
activeId={actionTab}
|
|
||||||
onSelect={(tabID) => setActionTab(tabID as ActionTab)}
|
|
||||||
layoutMode="fit"
|
|
||||||
variant="active-pill"
|
|
||||||
inactiveTabsIconOnly={isMobile}
|
|
||||||
className="h-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ScrollableOverlay
|
<ScrollableOverlay
|
||||||
as={ScrollShadow}
|
as={ScrollShadow}
|
||||||
ref={actionPanelScrollRef}
|
ref={actionPanelScrollRef}
|
||||||
@@ -2232,6 +2222,7 @@ export const GitView: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
onRevertFile={handleRevertFile}
|
onRevertFile={handleRevertFile}
|
||||||
isRevertingAll={isRevertingAll}
|
isRevertingAll={isRevertingAll}
|
||||||
|
onOpenStashes={() => setIsStashesDialogOpen(true)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CommitSection
|
<CommitSection
|
||||||
@@ -2269,54 +2260,44 @@ export const GitView: React.FC = () => {
|
|||||||
{actionTab === 'branch' ? (
|
{actionTab === 'branch' ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{canShowBranchWorkflows ? (
|
{canShowBranchWorkflows ? (
|
||||||
<BranchIntegrationSection
|
<>
|
||||||
mode="inline"
|
<BranchIntegrationSection
|
||||||
currentBranch={status?.current}
|
mode="inline"
|
||||||
localBranches={localBranches}
|
currentBranch={status?.current}
|
||||||
remoteBranches={remoteBranches}
|
localBranches={localBranches}
|
||||||
defaultTargetBranch={updateTargetBranch}
|
remoteBranches={remoteBranches}
|
||||||
onMerge={handleMerge}
|
defaultTargetBranch={updateTargetBranch}
|
||||||
onRebase={handleRebase}
|
onMerge={handleMerge}
|
||||||
disabled={isBusy}
|
onRebase={handleRebase}
|
||||||
isOperating={branchOperation !== null}
|
disabled={isBusy}
|
||||||
operationLogs={operationLogs}
|
isOperating={branchOperation !== null}
|
||||||
onOperationComplete={handleOperationComplete}
|
operationLogs={operationLogs}
|
||||||
/>
|
onOperationComplete={handleOperationComplete}
|
||||||
|
/>
|
||||||
|
{integrateCommitsProps ? (
|
||||||
|
<IntegrateCommitsSection
|
||||||
|
key={integrateCommitsProps.worktreeMetadata.path}
|
||||||
|
repoRoot={integrateCommitsProps.repoRoot}
|
||||||
|
sourceBranch={integrateCommitsProps.sourceBranch}
|
||||||
|
worktreeMetadata={integrateCommitsProps.worktreeMetadata}
|
||||||
|
localBranches={localBranches}
|
||||||
|
defaultTargetBranch={defaultTargetBranch}
|
||||||
|
refreshKey={integrateRefreshKey}
|
||||||
|
onRefresh={() => {
|
||||||
|
if (!currentDirectory) return;
|
||||||
|
fetchStatus(currentDirectory, git);
|
||||||
|
fetchBranches(currentDirectory, git);
|
||||||
|
fetchLog(currentDirectory, git, logMaxCountLocal);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="typography-meta text-muted-foreground">{t('gitView.branch.actionsUnavailable')}</p>
|
<p className="typography-meta text-muted-foreground">{t('gitView.branch.actionsUnavailable')}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{actionTab === 'worktree' ? (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{integrateCommitsProps ? (
|
|
||||||
<IntegrateCommitsSection
|
|
||||||
key={integrateCommitsProps.worktreeMetadata.path}
|
|
||||||
repoRoot={integrateCommitsProps.repoRoot}
|
|
||||||
sourceBranch={integrateCommitsProps.sourceBranch}
|
|
||||||
worktreeMetadata={integrateCommitsProps.worktreeMetadata}
|
|
||||||
localBranches={localBranches}
|
|
||||||
defaultTargetBranch={defaultTargetBranch}
|
|
||||||
refreshKey={integrateRefreshKey}
|
|
||||||
onRefresh={() => {
|
|
||||||
if (!currentDirectory) return;
|
|
||||||
fetchStatus(currentDirectory, git);
|
|
||||||
fetchBranches(currentDirectory, git);
|
|
||||||
fetchLog(currentDirectory, git, logMaxCountLocal);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="space-y-1 pt-3">
|
|
||||||
<div className="typography-ui-header font-semibold text-foreground">{t('gitView.integrate.title')}</div>
|
|
||||||
<div className="typography-micro text-muted-foreground">
|
|
||||||
{t('gitView.worktree.availableInWorktreeMode')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{actionTab === 'pr' ? (
|
{actionTab === 'pr' ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{pullRequestProps ? (
|
{pullRequestProps ? (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import { RiFolder3Fill, RiFolderOpenFill } from '@remixicon/react';
|
import { RiArchiveStackLine, RiFolder3Fill, RiFolderOpenFill } from '@remixicon/react';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
@@ -33,6 +33,7 @@ interface ChangesSectionProps {
|
|||||||
isRevertingAll?: boolean;
|
isRevertingAll?: boolean;
|
||||||
maxListHeightClassName?: string;
|
maxListHeightClassName?: string;
|
||||||
onVisiblePathsChange?: (paths: string[]) => void;
|
onVisiblePathsChange?: (paths: string[]) => void;
|
||||||
|
onOpenStashes?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHANGE_LIST_VIRTUALIZE_THRESHOLD = 120;
|
const CHANGE_LIST_VIRTUALIZE_THRESHOLD = 120;
|
||||||
@@ -183,6 +184,7 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
|
|||||||
isRevertingAll = false,
|
isRevertingAll = false,
|
||||||
maxListHeightClassName,
|
maxListHeightClassName,
|
||||||
onVisiblePathsChange,
|
onVisiblePathsChange,
|
||||||
|
onOpenStashes,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const scrollRef = React.useRef<HTMLDivElement | null>(null);
|
const scrollRef = React.useRef<HTMLDivElement | null>(null);
|
||||||
@@ -463,6 +465,19 @@ export const ChangesSection: React.FC<ChangesSectionProps> = ({
|
|||||||
<span className="typography-meta text-muted-foreground">{selectedCount}/{totalCount}</span>
|
<span className="typography-meta text-muted-foreground">{selectedCount}/{totalCount}</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{onOpenStashes ? (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="xs"
|
||||||
|
className="h-6 px-1.5"
|
||||||
|
onClick={onOpenStashes}
|
||||||
|
aria-label={t('gitView.stashes.title')}
|
||||||
|
title={t('gitView.stashes.title')}
|
||||||
|
>
|
||||||
|
<RiArchiveStackLine className="size-4" />
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 pr-1">
|
<div className="flex items-center gap-2 pr-1">
|
||||||
{totalCount > 0 && onRevertAll ? (
|
{totalCount > 0 && onRevertAll ? (
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import {
|
|||||||
RiCodeLine,
|
RiCodeLine,
|
||||||
RiHeartLine,
|
RiHeartLine,
|
||||||
RiHistoryLine,
|
RiHistoryLine,
|
||||||
RiArchiveStackLine,
|
|
||||||
RiUser3Line,
|
RiUser3Line,
|
||||||
} from '@remixicon/react';
|
} from '@remixicon/react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { SortableTabsStrip, type SortableTabsStripItem } from '@/components/ui/sortable-tabs-strip';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -49,7 +49,9 @@ interface GitHeaderProps {
|
|||||||
isApplyingIdentity: boolean;
|
isApplyingIdentity: boolean;
|
||||||
isWorktreeMode: boolean;
|
isWorktreeMode: boolean;
|
||||||
onOpenHistory?: () => void;
|
onOpenHistory?: () => void;
|
||||||
onOpenStashes?: () => void;
|
actionTabItems?: SortableTabsStripItem[];
|
||||||
|
activeActionTab?: string;
|
||||||
|
onSelectActionTab?: (tabID: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IDENTITY_ICON_MAP: Record<
|
const IDENTITY_ICON_MAP: Record<
|
||||||
@@ -208,7 +210,9 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
|||||||
isApplyingIdentity,
|
isApplyingIdentity,
|
||||||
isWorktreeMode,
|
isWorktreeMode,
|
||||||
onOpenHistory,
|
onOpenHistory,
|
||||||
onOpenStashes,
|
actionTabItems,
|
||||||
|
activeActionTab,
|
||||||
|
onSelectActionTab,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@@ -232,16 +236,6 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
|||||||
<TooltipContent sideOffset={8}>{t('gitView.history.title')}</TooltipContent>
|
<TooltipContent sideOffset={8}>{t('gitView.history.title')}</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : null}
|
) : null}
|
||||||
{onOpenStashes ? (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button variant="ghost" size="sm" className="h-8 w-8 px-0" onClick={onOpenStashes}>
|
|
||||||
<RiArchiveStackLine className="size-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent sideOffset={8}>{t('gitView.stashes.title')}</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -296,15 +290,28 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex shrink-0 items-center gap-1">
|
||||||
|
{managementButtons}
|
||||||
|
{identityControl}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-1.5 flex items-center justify-between gap-2 min-w-0">
|
{actionTabItems && activeActionTab && onSelectActionTab ? (
|
||||||
<div className="flex min-w-0 flex-wrap items-center gap-1">
|
<div className="mt-3 flex h-8 min-w-0 items-center gap-2">
|
||||||
{syncButtons}
|
<div className="min-w-0 flex-1">
|
||||||
{managementButtons}
|
<SortableTabsStrip
|
||||||
|
items={actionTabItems}
|
||||||
|
activeId={activeActionTab}
|
||||||
|
onSelect={onSelectActionTab}
|
||||||
|
layoutMode="fit"
|
||||||
|
variant="active-pill"
|
||||||
|
iconOnlyActiveTab={true}
|
||||||
|
className="h-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0">{syncButtons}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 max-w-[45%]">{identityControl}</div>
|
) : null}
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user