Skip to content

Commit 3bd2750

Browse files
TheodoreSpeaksTheodore Li
andauthored
fix(ui): ensure new resource tab button is always visible (#3633)
* fix(ui): ensure new resource tab button is always visible * Fix vertical scroll input scrolling tabs horizontally * Fix incorrect tool tip on file edit button * Fix lint, attach scroll listener to tabs themselves --------- Co-authored-by: Theodore Li <theo@sim.ai>
1 parent 70d8df5 commit 3bd2750

File tree

1 file changed

+104
-96
lines changed
  • apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs

1 file changed

+104
-96
lines changed

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-tabs/resource-tabs.tsx

Lines changed: 104 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ const PREVIEW_MODE_ICONS = {
4141
preview: Pencil,
4242
} satisfies Record<PreviewMode, (props: ComponentProps<typeof Eye>) => ReactNode>
4343

44+
const PREVIEW_MODE_LABELS: Record<PreviewMode, string> = {
45+
editor: 'Split Mode',
46+
split: 'Preview Mode',
47+
preview: 'Edit Mode',
48+
}
49+
4450
/**
4551
* Builds a `type:id` -> current name lookup from live query data so resource
4652
* tabs always reflect the latest name even after a rename.
@@ -273,103 +279,105 @@ export function ResourceTabs({
273279
<p>Collapse</p>
274280
</Tooltip.Content>
275281
</Tooltip.Root>
276-
<div
277-
ref={scrollNodeRef}
278-
className={cn(
279-
'flex min-w-0 flex-1 items-center overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
280-
RESOURCE_TAB_GAP_CLASS
281-
)}
282-
onDragOver={(e) => {
283-
e.preventDefault()
284-
startEdgeScroll(e.clientX)
285-
}}
286-
onDrop={handleDrop}
287-
>
288-
{resources.map((resource, idx) => {
289-
const config = getResourceConfig(resource.type)
290-
const displayName = nameLookup.get(`${resource.type}:${resource.id}`) ?? resource.title
291-
const isActive = activeId === resource.id
292-
const isHovered = hoveredTabId === resource.id
293-
const isDragging = draggedIdx === idx
294-
const showGapBefore =
295-
dropGapIdx === idx &&
296-
draggedIdx !== null &&
297-
draggedIdx !== idx &&
298-
draggedIdx !== idx - 1
299-
const showGapAfter =
300-
idx === resources.length - 1 &&
301-
dropGapIdx === resources.length &&
302-
draggedIdx !== null &&
303-
draggedIdx !== idx
282+
<div className={cn('flex min-w-0 flex-1 items-center', RESOURCE_TAB_GAP_CLASS)}>
283+
<div
284+
ref={scrollNodeRef}
285+
className={cn(
286+
'flex min-w-0 items-center overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
287+
RESOURCE_TAB_GAP_CLASS
288+
)}
289+
onDragOver={(e) => {
290+
e.preventDefault()
291+
startEdgeScroll(e.clientX)
292+
}}
293+
onDrop={handleDrop}
294+
>
295+
{resources.map((resource, idx) => {
296+
const config = getResourceConfig(resource.type)
297+
const displayName = nameLookup.get(`${resource.type}:${resource.id}`) ?? resource.title
298+
const isActive = activeId === resource.id
299+
const isHovered = hoveredTabId === resource.id
300+
const isDragging = draggedIdx === idx
301+
const showGapBefore =
302+
dropGapIdx === idx &&
303+
draggedIdx !== null &&
304+
draggedIdx !== idx &&
305+
draggedIdx !== idx - 1
306+
const showGapAfter =
307+
idx === resources.length - 1 &&
308+
dropGapIdx === resources.length &&
309+
draggedIdx !== null &&
310+
draggedIdx !== idx
304311

305-
return (
306-
<div key={resource.id} className='relative flex shrink-0 items-center'>
307-
{showGapBefore && (
308-
<div className='-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute top-1/2 left-0 z-10 h-[16px] w-[2px] rounded-full bg-[var(--text-subtle)]' />
309-
)}
310-
<Tooltip.Root>
311-
<Tooltip.Trigger asChild>
312-
<Button
313-
variant='subtle'
314-
draggable
315-
onDragStart={(e) => handleDragStart(e, idx)}
316-
onDragOver={(e) => handleDragOver(e, idx)}
317-
onDragLeave={handleDragLeave}
318-
onDragEnd={handleDragEnd}
319-
onMouseDown={(e) => {
320-
if (e.button === 1 && chatId) {
321-
e.preventDefault()
322-
handleRemove(e, resource)
323-
}
324-
}}
325-
onClick={() => onSelect(resource.id)}
326-
onMouseEnter={() => setHoveredTabId(resource.id)}
327-
onMouseLeave={() => setHoveredTabId(null)}
328-
className={cn(
329-
'group relative shrink-0 bg-transparent px-[8px] py-[4px] pr-[22px] text-[12px] transition-opacity duration-150',
330-
isActive && 'bg-[var(--surface-4)]',
331-
isDragging && 'opacity-30'
332-
)}
333-
>
334-
{config.renderTabIcon(resource, 'mr-[6px] h-[14px] w-[14px]')}
335-
{displayName}
336-
{(isHovered || isActive) && chatId && (
337-
<span
338-
role='button'
339-
tabIndex={-1}
340-
onClick={(e) => handleRemove(e, resource)}
341-
onKeyDown={(e) => {
342-
if (e.key === 'Enter')
343-
handleRemove(e as unknown as React.MouseEvent, resource)
344-
}}
345-
className='-translate-y-1/2 absolute top-1/2 right-[4px] flex items-center justify-center rounded-[4px] p-[1px] hover:bg-[var(--surface-5)]'
346-
aria-label={`Close ${displayName}`}
347-
>
348-
<svg
349-
className='h-[10px] w-[10px] text-[var(--text-icon)]'
350-
viewBox='0 0 24 24'
351-
fill='none'
352-
stroke='currentColor'
353-
strokeWidth='2.5'
354-
strokeLinecap='round'
355-
strokeLinejoin='round'
312+
return (
313+
<div key={resource.id} className='relative flex shrink-0 items-center'>
314+
{showGapBefore && (
315+
<div className='-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute top-1/2 left-0 z-10 h-[16px] w-[2px] rounded-full bg-[var(--text-subtle)]' />
316+
)}
317+
<Tooltip.Root>
318+
<Tooltip.Trigger asChild>
319+
<Button
320+
variant='subtle'
321+
draggable
322+
onDragStart={(e) => handleDragStart(e, idx)}
323+
onDragOver={(e) => handleDragOver(e, idx)}
324+
onDragLeave={handleDragLeave}
325+
onDragEnd={handleDragEnd}
326+
onMouseDown={(e) => {
327+
if (e.button === 1 && chatId) {
328+
e.preventDefault()
329+
handleRemove(e, resource)
330+
}
331+
}}
332+
onClick={() => onSelect(resource.id)}
333+
onMouseEnter={() => setHoveredTabId(resource.id)}
334+
onMouseLeave={() => setHoveredTabId(null)}
335+
className={cn(
336+
'group relative shrink-0 bg-transparent px-[8px] py-[4px] pr-[22px] text-[12px] transition-opacity duration-150',
337+
isActive && 'bg-[var(--surface-4)]',
338+
isDragging && 'opacity-30'
339+
)}
340+
>
341+
{config.renderTabIcon(resource, 'mr-[6px] h-[14px] w-[14px]')}
342+
{displayName}
343+
{(isHovered || isActive) && chatId && (
344+
<span
345+
role='button'
346+
tabIndex={-1}
347+
onClick={(e) => handleRemove(e, resource)}
348+
onKeyDown={(e) => {
349+
if (e.key === 'Enter')
350+
handleRemove(e as unknown as React.MouseEvent, resource)
351+
}}
352+
className='-translate-y-1/2 absolute top-1/2 right-[4px] flex items-center justify-center rounded-[4px] p-[1px] hover:bg-[var(--surface-5)]'
353+
aria-label={`Close ${displayName}`}
356354
>
357-
<path d='M18 6 6 18M6 6l12 12' />
358-
</svg>
359-
</span>
360-
)}
361-
</Button>
362-
</Tooltip.Trigger>
363-
<Tooltip.Content side='bottom'>
364-
<p>{displayName}</p>
365-
</Tooltip.Content>
366-
</Tooltip.Root>
367-
{showGapAfter && (
368-
<div className='-translate-y-1/2 pointer-events-none absolute top-1/2 right-0 z-10 h-[16px] w-[2px] translate-x-1/2 rounded-full bg-[var(--text-subtle)]' />
369-
)}
370-
</div>
371-
)
372-
})}
355+
<svg
356+
className='h-[10px] w-[10px] text-[var(--text-icon)]'
357+
viewBox='0 0 24 24'
358+
fill='none'
359+
stroke='currentColor'
360+
strokeWidth='2.5'
361+
strokeLinecap='round'
362+
strokeLinejoin='round'
363+
>
364+
<path d='M18 6 6 18M6 6l12 12' />
365+
</svg>
366+
</span>
367+
)}
368+
</Button>
369+
</Tooltip.Trigger>
370+
<Tooltip.Content side='bottom'>
371+
<p>{displayName}</p>
372+
</Tooltip.Content>
373+
</Tooltip.Root>
374+
{showGapAfter && (
375+
<div className='-translate-y-1/2 pointer-events-none absolute top-1/2 right-0 z-10 h-[16px] w-[2px] translate-x-1/2 rounded-full bg-[var(--text-subtle)]' />
376+
)}
377+
</div>
378+
)
379+
})}
380+
</div>
373381
{chatId && (
374382
<AddResourceDropdown
375383
workspaceId={workspaceId}
@@ -395,7 +403,7 @@ export function ResourceTabs({
395403
</Button>
396404
</Tooltip.Trigger>
397405
<Tooltip.Content side='bottom'>
398-
<p>Preview mode</p>
406+
<p>{PREVIEW_MODE_LABELS[previewMode]}</p>
399407
</Tooltip.Content>
400408
</Tooltip.Root>
401409
)}

0 commit comments

Comments
 (0)