Skip to content

Commit 14089f7

Browse files
authored
v0.6.14: performance improvements, connectors UX, collapsed sidebar actions
2 parents e615816 + b90bb75 commit 14089f7

File tree

48 files changed

+1405
-776
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1405
-776
lines changed

apps/sim/app/_styles/globals.css

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
188188
--border-1: #e0e0e0; /* stronger border */
189189
--surface-6: #e5e5e5; /* popovers, elevated surfaces */
190190
--surface-7: #d9d9d9;
191-
--surface-active: #ececec; /* hover/active state */
191+
--surface-hover: #f2f2f2; /* hover state */
192+
--surface-active: #ececec; /* active/selected state */
192193

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

@@ -342,7 +343,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn {
342343
--border-1: #3d3d3d;
343344
--surface-6: #454545;
344345
--surface-7: #505050;
345-
--surface-active: #2c2c2c; /* hover/active state */
346+
--surface-hover: #262626; /* hover state */
347+
--surface-active: #2c2c2c; /* active/selected state */
346348

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

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

504-
body {
505-
@apply antialiased;
506-
}
507506
::-webkit-scrollbar {
508507
width: var(--scrollbar-size);
509508
height: var(--scrollbar-size);

apps/sim/app/workspace/[workspaceId]/components/resource/components/owner-cell/owner-cell.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import { memo } from 'react'
12
import type { ResourceCell } from '@/app/workspace/[workspaceId]/components/resource/resource'
23
import type { WorkspaceMember } from '@/hooks/queries/workspace'
34

4-
function OwnerAvatar({ name, image }: { name: string; image: string | null }) {
5+
interface OwnerAvatarProps {
6+
name: string
7+
image: string | null
8+
}
9+
10+
const OwnerAvatar = memo(function OwnerAvatar({ name, image }: OwnerAvatarProps) {
511
if (image) {
612
return (
713
<img
@@ -18,7 +24,7 @@ function OwnerAvatar({ name, image }: { name: string; image: string | null }) {
1824
{name.charAt(0).toUpperCase()}
1925
</span>
2026
)
21-
}
27+
})
2228

2329
/**
2430
* Resolves a user ID into a ResourceCell with an avatar icon and display name.

apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
import { cn } from '@/lib/core/utils/cn'
1212
import { InlineRenameInput } from '@/app/workspace/[workspaceId]/components/inline-rename-input'
1313

14+
const HEADER_PLUS_ICON = <Plus className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
15+
1416
export interface DropdownOption {
1517
label: string
1618
icon?: React.ElementType
@@ -122,7 +124,7 @@ export const ResourceHeader = memo(function ResourceHeader({
122124
variant='subtle'
123125
className='px-2 py-1 text-caption'
124126
>
125-
<Plus className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
127+
{HEADER_PLUS_ICON}
126128
{create.label}
127129
</Button>
128130
)}
@@ -132,19 +134,21 @@ export const ResourceHeader = memo(function ResourceHeader({
132134
)
133135
})
134136

135-
function BreadcrumbSegment({
136-
icon: Icon,
137-
label,
138-
onClick,
139-
dropdownItems,
140-
editing,
141-
}: {
137+
interface BreadcrumbSegmentProps {
142138
icon?: React.ElementType
143139
label: string
144140
onClick?: () => void
145141
dropdownItems?: DropdownOption[]
146142
editing?: BreadcrumbEditing
147-
}) {
143+
}
144+
145+
const BreadcrumbSegment = memo(function BreadcrumbSegment({
146+
icon: Icon,
147+
label,
148+
onClick,
149+
dropdownItems,
150+
editing,
151+
}: BreadcrumbSegmentProps) {
148152
if (editing?.isEditing) {
149153
return (
150154
<span className='inline-flex items-center px-2 py-1'>
@@ -203,4 +207,4 @@ function BreadcrumbSegment({
203207
{content}
204208
</span>
205209
)
206-
}
210+
})

apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx

Lines changed: 92 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { memo, type ReactNode } from 'react'
1+
import { memo, type ReactNode, useCallback, useRef, useState } from 'react'
22
import * as PopoverPrimitive from '@radix-ui/react-popover'
33
import {
44
ArrowDown,
@@ -16,6 +16,12 @@ import {
1616
} from '@/components/emcn'
1717
import { cn } from '@/lib/core/utils/cn'
1818

19+
const SEARCH_ICON = (
20+
<Search className='pointer-events-none h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
21+
)
22+
const FILTER_ICON = <ListFilter className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
23+
const SORT_ICON = <ArrowUpDown className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
24+
1925
type SortDirection = 'asc' | 'desc'
2026

2127
export interface ColumnOption {
@@ -79,56 +85,7 @@ export const ResourceOptionsBar = memo(function ResourceOptionsBar({
7985
return (
8086
<div className={cn('border-[var(--border)] border-b py-2.5', search ? 'px-6' : 'px-4')}>
8187
<div className='flex items-center justify-between'>
82-
{search && (
83-
<div className='relative flex flex-1 items-center'>
84-
<Search className='pointer-events-none h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
85-
<div className='flex flex-1 items-center gap-1.5 overflow-x-auto pl-2.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'>
86-
{search.tags?.map((tag, i) => (
87-
<Button
88-
key={`${tag.label}-${tag.value}-${i}`}
89-
variant='subtle'
90-
className={cn(
91-
'shrink-0 px-2 py-1 text-caption',
92-
search.highlightedTagIndex === i &&
93-
'ring-1 ring-[var(--border-focus)] ring-offset-1'
94-
)}
95-
onClick={tag.onRemove}
96-
>
97-
{tag.label}: {tag.value}
98-
<span className='ml-1 text-[var(--text-icon)] text-micro'></span>
99-
</Button>
100-
))}
101-
<input
102-
ref={search.inputRef}
103-
type='text'
104-
value={search.value}
105-
onChange={(e) => search.onChange(e.target.value)}
106-
onKeyDown={search.onKeyDown}
107-
onFocus={search.onFocus}
108-
onBlur={search.onBlur}
109-
placeholder={search.tags?.length ? '' : (search.placeholder ?? 'Search...')}
110-
className='min-w-[80px] flex-1 bg-transparent py-1 text-[var(--text-secondary)] text-caption outline-none placeholder:text-[var(--text-subtle)]'
111-
/>
112-
</div>
113-
{search.tags?.length || search.value ? (
114-
<button
115-
type='button'
116-
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)]'
117-
onClick={search.onClearAll}
118-
>
119-
<span className='text-caption'></span>
120-
</button>
121-
) : null}
122-
{search.dropdown && (
123-
<div
124-
ref={search.dropdownRef}
125-
className='absolute top-full left-0 z-50 mt-1.5 w-full rounded-lg border border-[var(--border)] bg-[var(--bg)] shadow-sm'
126-
>
127-
{search.dropdown}
128-
</div>
129-
)}
130-
</div>
131-
)}
88+
{search && <SearchSection search={search} />}
13289
<div className='flex items-center gap-1.5'>
13390
{extras}
13491
{filterTags?.map((tag) => (
@@ -146,7 +103,7 @@ export const ResourceOptionsBar = memo(function ResourceOptionsBar({
146103
<PopoverPrimitive.Root>
147104
<PopoverPrimitive.Trigger asChild>
148105
<Button variant='subtle' className='px-2 py-1 text-caption'>
149-
<ListFilter className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
106+
{FILTER_ICON}
150107
Filter
151108
</Button>
152109
</PopoverPrimitive.Trigger>
@@ -170,14 +127,94 @@ export const ResourceOptionsBar = memo(function ResourceOptionsBar({
170127
)
171128
})
172129

173-
function SortDropdown({ config }: { config: SortConfig }) {
130+
const SearchSection = memo(function SearchSection({ search }: { search: SearchConfig }) {
131+
const [localValue, setLocalValue] = useState(search.value)
132+
133+
const lastReportedRef = useRef(search.value)
134+
135+
if (search.value !== lastReportedRef.current) {
136+
setLocalValue(search.value)
137+
lastReportedRef.current = search.value
138+
}
139+
140+
const handleInputChange = useCallback(
141+
(e: React.ChangeEvent<HTMLInputElement>) => {
142+
const next = e.target.value
143+
setLocalValue(next)
144+
search.onChange(next)
145+
},
146+
[search.onChange]
147+
)
148+
149+
const handleClearAll = useCallback(() => {
150+
setLocalValue('')
151+
lastReportedRef.current = ''
152+
if (search.onClearAll) {
153+
search.onClearAll()
154+
} else {
155+
search.onChange('')
156+
}
157+
}, [search.onClearAll, search.onChange])
158+
159+
return (
160+
<div className='relative flex flex-1 items-center'>
161+
{SEARCH_ICON}
162+
<div className='flex flex-1 items-center gap-1.5 overflow-x-auto pl-2.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'>
163+
{search.tags?.map((tag, i) => (
164+
<Button
165+
key={`${tag.label}-${tag.value}-${i}`}
166+
variant='subtle'
167+
className={cn(
168+
'shrink-0 px-2 py-1 text-caption',
169+
search.highlightedTagIndex === i && 'ring-1 ring-[var(--border-focus)] ring-offset-1'
170+
)}
171+
onClick={tag.onRemove}
172+
>
173+
{tag.label}: {tag.value}
174+
<span className='ml-1 text-[var(--text-icon)] text-micro'></span>
175+
</Button>
176+
))}
177+
<input
178+
ref={search.inputRef}
179+
type='text'
180+
value={localValue}
181+
onChange={handleInputChange}
182+
onKeyDown={search.onKeyDown}
183+
onFocus={search.onFocus}
184+
onBlur={search.onBlur}
185+
placeholder={search.tags?.length ? '' : (search.placeholder ?? 'Search...')}
186+
className='min-w-[80px] flex-1 bg-transparent py-1 text-[var(--text-secondary)] text-caption outline-none placeholder:text-[var(--text-subtle)]'
187+
/>
188+
</div>
189+
{search.tags?.length || localValue ? (
190+
<button
191+
type='button'
192+
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)]'
193+
onClick={handleClearAll}
194+
>
195+
<span className='text-caption'></span>
196+
</button>
197+
) : null}
198+
{search.dropdown && (
199+
<div
200+
ref={search.dropdownRef}
201+
className='absolute top-full left-0 z-50 mt-1.5 w-full rounded-lg border border-[var(--border)] bg-[var(--bg)] shadow-sm'
202+
>
203+
{search.dropdown}
204+
</div>
205+
)}
206+
</div>
207+
)
208+
})
209+
210+
const SortDropdown = memo(function SortDropdown({ config }: { config: SortConfig }) {
174211
const { options, active, onSort, onClear } = config
175212

176213
return (
177214
<DropdownMenu>
178215
<DropdownMenuTrigger asChild>
179216
<Button variant='subtle' className='px-2 py-1 text-caption'>
180-
<ArrowUpDown className='mr-1.5 h-[14px] w-[14px] text-[var(--text-icon)]' />
217+
{SORT_ICON}
181218
Sort
182219
</Button>
183220
</DropdownMenuTrigger>
@@ -218,4 +255,4 @@ function SortDropdown({ config }: { config: SortConfig }) {
218255
</DropdownMenuContent>
219256
</DropdownMenu>
220257
)
221-
}
258+
})

0 commit comments

Comments
 (0)