Skip to content
9 changes: 4 additions & 5 deletions apps/sim/app/_styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--border-1: #e0e0e0; /* stronger border */
--surface-6: #e5e5e5; /* popovers, elevated surfaces */
--surface-7: #d9d9d9;
--surface-active: #ececec; /* hover/active state */
--surface-hover: #f2f2f2; /* hover state */
--surface-active: #ececec; /* active/selected state */

--workflow-edge: #e0e0e0; /* workflow handles/edges - matches border-1 */

Expand Down Expand Up @@ -342,7 +343,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
--border-1: #3d3d3d;
--surface-6: #454545;
--surface-7: #505050;
--surface-active: #2c2c2c; /* hover/active state */
--surface-hover: #262626; /* hover state */
--surface-active: #2c2c2c; /* active/selected state */

--workflow-edge: #454545; /* workflow handles/edges - same as surface-6 in dark */

Expand Down Expand Up @@ -501,9 +503,6 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
caret-color: var(--text-primary);
}

body {
@apply antialiased;
}
::-webkit-scrollbar {
width: var(--scrollbar-size);
height: var(--scrollbar-size);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { memo } from 'react'
import type { ResourceCell } from '@/app/workspace/[workspaceId]/components/resource/resource'
import type { WorkspaceMember } from '@/hooks/queries/workspace'

function OwnerAvatar({ name, image }: { name: string; image: string | null }) {
interface OwnerAvatarProps {
name: string
image: string | null
}

const OwnerAvatar = memo(function OwnerAvatar({ name, image }: OwnerAvatarProps) {
if (image) {
return (
<img
Expand All @@ -18,7 +24,7 @@ function OwnerAvatar({ name, image }: { name: string; image: string | null }) {
{name.charAt(0).toUpperCase()}
</span>
)
}
})

/**
* Resolves a user ID into a ResourceCell with an avatar icon and display name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
import { cn } from '@/lib/core/utils/cn'
import { InlineRenameInput } from '@/app/workspace/[workspaceId]/components/inline-rename-input'

const HEADER_PLUS_ICON = <Plus className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />

export interface DropdownOption {
label: string
icon?: React.ElementType
Expand Down Expand Up @@ -122,7 +124,7 @@ export const ResourceHeader = memo(function ResourceHeader({
variant='subtle'
className='px-2 py-1 text-caption'
>
<Plus className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
{HEADER_PLUS_ICON}
{create.label}
</Button>
)}
Expand All @@ -132,19 +134,21 @@ export const ResourceHeader = memo(function ResourceHeader({
)
})

function BreadcrumbSegment({
icon: Icon,
label,
onClick,
dropdownItems,
editing,
}: {
interface BreadcrumbSegmentProps {
icon?: React.ElementType
label: string
onClick?: () => void
dropdownItems?: DropdownOption[]
editing?: BreadcrumbEditing
}) {
}

const BreadcrumbSegment = memo(function BreadcrumbSegment({
icon: Icon,
label,
onClick,
dropdownItems,
editing,
}: BreadcrumbSegmentProps) {
if (editing?.isEditing) {
return (
<span className='inline-flex items-center px-2 py-1'>
Expand Down Expand Up @@ -203,4 +207,4 @@ function BreadcrumbSegment({
{content}
</span>
)
}
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, type ReactNode } from 'react'
import { memo, type ReactNode, useCallback, useRef, useState } from 'react'
import * as PopoverPrimitive from '@radix-ui/react-popover'
import {
ArrowDown,
Expand All @@ -16,6 +16,12 @@ import {
} from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'

const SEARCH_ICON = (
<Search className='pointer-events-none h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
)
const FILTER_ICON = <ListFilter className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
const SORT_ICON = <ArrowUpDown className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />

type SortDirection = 'asc' | 'desc'

export interface ColumnOption {
Expand Down Expand Up @@ -79,56 +85,7 @@ export const ResourceOptionsBar = memo(function ResourceOptionsBar({
return (
<div className={cn('border-[var(--border)] border-b py-2.5', search ? 'px-6' : 'px-4')}>
<div className='flex items-center justify-between'>
{search && (
<div className='relative flex flex-1 items-center'>
<Search className='pointer-events-none h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
<div className='flex flex-1 items-center gap-1.5 overflow-x-auto pl-2.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'>
{search.tags?.map((tag, i) => (
<Button
key={`${tag.label}-${tag.value}-${i}`}
variant='subtle'
className={cn(
'shrink-0 px-2 py-1 text-caption',
search.highlightedTagIndex === i &&
'ring-1 ring-[var(--border-focus)] ring-offset-1'
)}
onClick={tag.onRemove}
>
{tag.label}: {tag.value}
<span className='ml-1 text-[var(--text-icon)] text-micro'>✕</span>
</Button>
))}
<input
ref={search.inputRef}
type='text'
value={search.value}
onChange={(e) => search.onChange(e.target.value)}
onKeyDown={search.onKeyDown}
onFocus={search.onFocus}
onBlur={search.onBlur}
placeholder={search.tags?.length ? '' : (search.placeholder ?? 'Search...')}
className='min-w-[80px] flex-1 bg-transparent py-1 text-[var(--text-secondary)] text-caption outline-none placeholder:text-[var(--text-subtle)]'
/>
</div>
{search.tags?.length || search.value ? (
<button
type='button'
className='mr-0.5 flex h-[14px] w-[14px] shrink-0 items-center justify-center text-[var(--text-subtle)] transition-colors hover-hover:text-[var(--text-secondary)]'
onClick={search.onClearAll}
>
<span className='text-caption'>✕</span>
</button>
) : null}
{search.dropdown && (
<div
ref={search.dropdownRef}
className='absolute top-full left-0 z-50 mt-1.5 w-full rounded-lg border border-[var(--border)] bg-[var(--bg)] shadow-sm'
>
{search.dropdown}
</div>
)}
</div>
)}
{search && <SearchSection search={search} />}
<div className='flex items-center gap-1.5'>
{extras}
{filterTags?.map((tag) => (
Expand All @@ -146,7 +103,7 @@ export const ResourceOptionsBar = memo(function ResourceOptionsBar({
<PopoverPrimitive.Root>
<PopoverPrimitive.Trigger asChild>
<Button variant='subtle' className='px-2 py-1 text-caption'>
<ListFilter className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
{FILTER_ICON}
Filter
</Button>
</PopoverPrimitive.Trigger>
Expand All @@ -170,14 +127,94 @@ export const ResourceOptionsBar = memo(function ResourceOptionsBar({
)
})

function SortDropdown({ config }: { config: SortConfig }) {
const SearchSection = memo(function SearchSection({ search }: { search: SearchConfig }) {
const [localValue, setLocalValue] = useState(search.value)

const lastReportedRef = useRef(search.value)

if (search.value !== lastReportedRef.current) {
setLocalValue(search.value)
lastReportedRef.current = search.value
}

const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const next = e.target.value
setLocalValue(next)
search.onChange(next)
},
[search.onChange]
)

const handleClearAll = useCallback(() => {
setLocalValue('')
lastReportedRef.current = ''
if (search.onClearAll) {
search.onClearAll()
} else {
search.onChange('')
}
}, [search.onClearAll, search.onChange])

return (
<div className='relative flex flex-1 items-center'>
{SEARCH_ICON}
<div className='flex flex-1 items-center gap-1.5 overflow-x-auto pl-2.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'>
{search.tags?.map((tag, i) => (
<Button
key={`${tag.label}-${tag.value}-${i}`}
variant='subtle'
className={cn(
'shrink-0 px-2 py-1 text-caption',
search.highlightedTagIndex === i && 'ring-1 ring-[var(--border-focus)] ring-offset-1'
)}
onClick={tag.onRemove}
>
{tag.label}: {tag.value}
<span className='ml-1 text-[var(--text-icon)] text-micro'>✕</span>
</Button>
))}
<input
ref={search.inputRef}
type='text'
value={localValue}
onChange={handleInputChange}
onKeyDown={search.onKeyDown}
onFocus={search.onFocus}
onBlur={search.onBlur}
placeholder={search.tags?.length ? '' : (search.placeholder ?? 'Search...')}
className='min-w-[80px] flex-1 bg-transparent py-1 text-[var(--text-secondary)] text-caption outline-none placeholder:text-[var(--text-subtle)]'
/>
</div>
{search.tags?.length || localValue ? (
<button
type='button'
className='mr-0.5 flex h-[14px] w-[14px] shrink-0 items-center justify-center text-[var(--text-subtle)] transition-colors hover-hover:text-[var(--text-secondary)]'
onClick={handleClearAll}
>
<span className='text-caption'>✕</span>
</button>
) : null}
{search.dropdown && (
<div
ref={search.dropdownRef}
className='absolute top-full left-0 z-50 mt-1.5 w-full rounded-lg border border-[var(--border)] bg-[var(--bg)] shadow-sm'
>
{search.dropdown}
</div>
)}
</div>
)
})

const SortDropdown = memo(function SortDropdown({ config }: { config: SortConfig }) {
const { options, active, onSort, onClear } = config

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='subtle' className='px-2 py-1 text-caption'>
<ArrowUpDown className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
{SORT_ICON}
Sort
</Button>
</DropdownMenuTrigger>
Expand Down Expand Up @@ -218,4 +255,4 @@ function SortDropdown({ config }: { config: SortConfig }) {
</DropdownMenuContent>
</DropdownMenu>
)
}
})
Loading
Loading