Skip to content

Commit d9c1a53

Browse files
committed
improvement(resource): layout
1 parent 2bdc073 commit d9c1a53

File tree

19 files changed

+383
-642
lines changed

19 files changed

+383
-642
lines changed

apps/sim/app/workspace/[workspaceId]/components/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export { ErrorState, type ErrorStateProps } from './error'
22
export { ownerCell } from './resource/components/owner-cell/owner-cell'
3-
export type { BreadcrumbItem } from './resource/components/resource-header'
3+
export type {
4+
BreadcrumbItem,
5+
CreateAction,
6+
HeaderAction,
7+
} from './resource/components/resource-header'
48
export { ResourceHeader } from './resource/components/resource-header'
59
export { ResourceOptionsBar } from './resource/components/resource-options-bar'
610
export { timeCell } from './resource/components/time-cell/time-cell'
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export type { BreadcrumbItem } from './resource-header'
1+
export type { BreadcrumbItem, CreateAction, HeaderAction } from './resource-header'
22
export { ResourceHeader } from './resource-header'

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

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,33 @@ export interface BreadcrumbItem {
77
onClick?: () => void
88
}
99

10+
export interface HeaderAction {
11+
label: string
12+
icon?: React.ElementType
13+
onClick: () => void
14+
}
15+
16+
export interface CreateAction {
17+
label: string
18+
onClick: () => void
19+
disabled?: boolean
20+
}
21+
1022
interface ResourceHeaderProps {
1123
icon?: React.ElementType
1224
title?: string
1325
breadcrumbs?: BreadcrumbItem[]
14-
create?: {
15-
label: string
16-
onClick: () => void
17-
disabled?: boolean
18-
}
26+
create?: CreateAction
27+
actions?: HeaderAction[]
1928
}
2029

21-
export function ResourceHeader({ icon: Icon, title, breadcrumbs, create }: ResourceHeaderProps) {
30+
export function ResourceHeader({
31+
icon: Icon,
32+
title,
33+
breadcrumbs,
34+
create,
35+
actions,
36+
}: ResourceHeaderProps) {
2237
const hasBreadcrumbs = breadcrumbs && breadcrumbs.length > 0
2338

2439
return (
@@ -52,17 +67,33 @@ export function ResourceHeader({ icon: Icon, title, breadcrumbs, create }: Resou
5267
</>
5368
)}
5469
</div>
55-
{create && (
56-
<Button
57-
onClick={create.onClick}
58-
disabled={create.disabled}
59-
variant='subtle'
60-
className='px-[8px] py-[4px] text-[12px]'
61-
>
62-
<Plus className='mr-[6px] h-[14px] w-[14px]' />
63-
{create.label}
64-
</Button>
65-
)}
70+
<div className='flex items-center gap-[6px]'>
71+
{actions?.map((action) => {
72+
const ActionIcon = action.icon
73+
return (
74+
<Button
75+
key={action.label}
76+
onClick={action.onClick}
77+
variant='subtle'
78+
className='px-[8px] py-[4px] text-[12px]'
79+
>
80+
{ActionIcon && <ActionIcon className='mr-[6px] h-[14px] w-[14px]' />}
81+
{action.label}
82+
</Button>
83+
)
84+
})}
85+
{create && (
86+
<Button
87+
onClick={create.onClick}
88+
disabled={create.disabled}
89+
variant='subtle'
90+
className='px-[8px] py-[4px] text-[12px]'
91+
>
92+
<Plus className='mr-[6px] h-[14px] w-[14px]' />
93+
{create.label}
94+
</Button>
95+
)}
96+
</div>
6697
</div>
6798
</div>
6899
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export type { ActiveFilter, ColumnOption, FilterConfig, SortConfig } from './resource-options-bar'
1+
export type { ColumnOption, SortConfig } from './resource-options-bar'
22
export { ResourceOptionsBar } from './resource-options-bar'
Lines changed: 25 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import type { ReactNode } from 'react'
2+
import * as PopoverPrimitive from '@radix-ui/react-popover'
23
import {
34
ArrowDown,
45
ArrowUp,
56
ArrowUpDown,
67
Button,
78
DropdownMenu,
8-
DropdownMenuCheckboxItem,
99
DropdownMenuContent,
1010
DropdownMenuItem,
1111
DropdownMenuSeparator,
12-
DropdownMenuSub,
13-
DropdownMenuSubContent,
14-
DropdownMenuSubTrigger,
1512
DropdownMenuTrigger,
1613
ListFilter,
1714
Search,
@@ -34,46 +31,18 @@ export interface SortConfig {
3431
onClear?: () => void
3532
}
3633

37-
export interface ActiveFilter {
38-
column: string
39-
operator: string
40-
}
41-
42-
export interface FilterConfig {
43-
options: ColumnOption[]
44-
active: ActiveFilter[]
45-
onToggle: (column: string, operator: string) => void
46-
onClear?: () => void
47-
}
48-
49-
const DEFAULT_FILTER_OPERATORS = [
50-
{ id: 'empty', label: 'Is empty' },
51-
{ id: 'not_empty', label: 'Is not empty' },
52-
] as const
53-
54-
const BOOLEAN_FILTER_OPERATORS = [
55-
{ id: 'eq_true', label: 'Is true' },
56-
{ id: 'eq_false', label: 'Is false' },
57-
] as const
58-
5934
interface ResourceOptionsBarProps {
6035
search?: {
6136
value: string
6237
onChange: (value: string) => void
6338
placeholder?: string
6439
}
6540
sort?: SortConfig
66-
filter?: FilterConfig
67-
toolbarActions?: ReactNode
41+
filter?: ReactNode
6842
}
6943

70-
export function ResourceOptionsBar({
71-
search,
72-
sort,
73-
filter,
74-
toolbarActions,
75-
}: ResourceOptionsBarProps) {
76-
const hasContent = search || sort || filter || toolbarActions
44+
export function ResourceOptionsBar({ search, sort, filter }: ResourceOptionsBarProps) {
45+
const hasContent = search || sort || filter
7746
if (!hasContent) return null
7847

7948
return (
@@ -97,9 +66,28 @@ export function ResourceOptionsBar({
9766
</div>
9867
)}
9968
<div className='flex items-center gap-[6px]'>
100-
{filter && <FilterDropdown config={filter} />}
69+
{filter && (
70+
<PopoverPrimitive.Root>
71+
<PopoverPrimitive.Trigger asChild>
72+
<Button variant='subtle' className='px-[8px] py-[4px] text-[12px]'>
73+
<ListFilter className='mr-[6px] h-[14px] w-[14px] text-[var(--text-icon)]' />
74+
Filter
75+
</Button>
76+
</PopoverPrimitive.Trigger>
77+
<PopoverPrimitive.Portal>
78+
<PopoverPrimitive.Content
79+
align='start'
80+
sideOffset={6}
81+
className={cn(
82+
'z-50 rounded-[8px] border border-[var(--border)] bg-white shadow-sm dark:bg-[var(--bg)]'
83+
)}
84+
>
85+
{filter}
86+
</PopoverPrimitive.Content>
87+
</PopoverPrimitive.Portal>
88+
</PopoverPrimitive.Root>
89+
)}
10190
{sort && <SortDropdown config={sort} />}
102-
{toolbarActions}
10391
</div>
10492
</div>
10593
</div>
@@ -154,57 +142,3 @@ function SortDropdown({ config }: { config: SortConfig }) {
154142
</DropdownMenu>
155143
)
156144
}
157-
158-
function FilterDropdown({ config }: { config: FilterConfig }) {
159-
const { options, active, onToggle, onClear } = config
160-
161-
return (
162-
<DropdownMenu>
163-
<DropdownMenuTrigger asChild>
164-
<Button variant='subtle' className='px-[8px] py-[4px] text-[12px]'>
165-
<ListFilter className='mr-[6px] h-[14px] w-[14px] text-[var(--text-icon)]' />
166-
Filter
167-
</Button>
168-
</DropdownMenuTrigger>
169-
<DropdownMenuContent align='end'>
170-
{options.map((option) => {
171-
const operators =
172-
option.type === 'boolean' ? BOOLEAN_FILTER_OPERATORS : DEFAULT_FILTER_OPERATORS
173-
const activeFilter = active.find((f) => f.column === option.id)
174-
const Icon = option.icon
175-
176-
return (
177-
<DropdownMenuSub key={option.id}>
178-
<DropdownMenuSubTrigger>
179-
{Icon && <Icon />}
180-
{option.label}
181-
{activeFilter && (
182-
<span className='ml-auto h-[6px] w-[6px] rounded-full bg-[var(--text-tertiary)]' />
183-
)}
184-
</DropdownMenuSubTrigger>
185-
<DropdownMenuSubContent>
186-
{operators.map((op) => (
187-
<DropdownMenuCheckboxItem
188-
key={op.id}
189-
checked={activeFilter?.operator === op.id}
190-
onCheckedChange={() => onToggle(option.id, op.id)}
191-
>
192-
{op.label}
193-
</DropdownMenuCheckboxItem>
194-
))}
195-
</DropdownMenuSubContent>
196-
</DropdownMenuSub>
197-
)
198-
})}
199-
{active.length > 0 && onClear && (
200-
<>
201-
<DropdownMenuSeparator />
202-
<DropdownMenuItem onSelect={onClear} className='text-[var(--text-tertiary)]'>
203-
Clear all filters
204-
</DropdownMenuItem>
205-
</>
206-
)}
207-
</DropdownMenuContent>
208-
</DropdownMenu>
209-
)
210-
}

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ReactNode } from 'react'
44
import { useCallback, useMemo, useRef, useState } from 'react'
55
import { ArrowDown, ArrowUp, Button, Plus, Skeleton } from '@/components/emcn'
66
import { cn } from '@/lib/core/utils/cn'
7+
import type { CreateAction, HeaderAction } from './components/resource-header'
78
import { ResourceHeader } from './components/resource-header'
89
import type { SortConfig } from './components/resource-options-bar'
910
import { ResourceOptionsBar } from './components/resource-options-bar'
@@ -27,18 +28,14 @@ export interface ResourceRow {
2728
interface ResourceProps {
2829
icon: React.ElementType
2930
title: string
30-
create?: {
31-
label: string
32-
onClick: () => void
33-
disabled?: boolean
34-
}
31+
create?: CreateAction
3532
search?: {
3633
value: string
3734
onChange: (value: string) => void
3835
placeholder?: string
3936
}
4037
defaultSort: string
41-
toolbarActions?: ReactNode
38+
headerActions?: HeaderAction[]
4239
columns: ResourceColumn[]
4340
rows: ResourceRow[]
4441
onRowClick?: (rowId: string) => void
@@ -60,7 +57,7 @@ export function Resource({
6057
create,
6158
search,
6259
defaultSort,
63-
toolbarActions,
60+
headerActions,
6461
columns,
6562
rows,
6663
onRowClick,
@@ -112,8 +109,8 @@ export function Resource({
112109
className='flex h-full flex-1 flex-col overflow-hidden bg-white dark:bg-[var(--bg)]'
113110
onContextMenu={onContextMenu}
114111
>
115-
<ResourceHeader icon={icon} title={title} create={create} />
116-
<ResourceOptionsBar search={search} sort={sortConfig} toolbarActions={toolbarActions} />
112+
<ResourceHeader icon={icon} title={title} create={create} actions={headerActions} />
113+
<ResourceOptionsBar search={search} sort={sortConfig} />
117114

118115
{isLoading ? (
119116
<DataTableSkeleton columns={columns} rowCount={loadingRows} />

0 commit comments

Comments
 (0)