Skip to content

Commit cae1769

Browse files
authored
improvement(knowledge): align connected-sources rows and move source chip left of filter/sort (#5117)
* improvement(knowledge): align connected-sources rows and move source chip left of filter/sort - Drop the -mx-2 on the connectors list so rows respect the ChipModalBody gutter: the row hover no longer bleeds to the modal edges and row content lines up with the px-4 header. - Add a 'leading' slot to ResourceOptions (left of the filter/sort cluster) and render the knowledge connected-source chip there instead of the far-right 'aside', so it reads as part of the control row. 'aside' stays right-aligned for the table editor's run/stop control. * improvement(resource): render options aside left of filter/sort The options-bar aside has a single other consumer (the table editor's embedded run/stop control), so instead of adding a separate slot, render aside itself to the left of the filter/sort cluster. Drops the extra slot and keeps one canonical control position; the run/stop control moves left too, which is fine for a status widget. * fix(resource): keep options aside grouped with filter/sort without a search bar Group aside + the filter/sort cluster in one ml-auto right-aligned container instead of relying on the search's flex-1 to anchor them. Without this, an options bar with no search (the embedded mothership table editor) split aside to the far left and filter/sort to the far right via justify-between. * docs(resource): clarify aside groups with filter/sort regardless of search
1 parent 4d39b0c commit cae1769

3 files changed

Lines changed: 69 additions & 65 deletions

File tree

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

Lines changed: 65 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ interface ResourceOptionsProps {
8383
filter?: FilterConfig
8484
filterTags?: FilterTag[]
8585
/**
86-
* Supplementary right-aligned slot (pushed opposite the left-aligned
87-
* filter/sort via `justify-between`) for lightweight status content — e.g.
88-
* the knowledge list's connector badges or the table editor's run/stop
89-
* control in embedded mode. Keep it to badges/status widgets; primary
90-
* actions belong in the header's `actions`, not here.
86+
* Lightweight control rendered immediately to the LEFT of the filter/sort
87+
* cluster; the two form one right-aligned group, with or without a search —
88+
* e.g. the knowledge view's connected-source badge or the table editor's
89+
* embedded run/stop control. Keep it to badges/status widgets; primary actions
90+
* belong in the header's `actions`.
9191
*/
9292
aside?: ReactNode
9393
}
@@ -115,64 +115,68 @@ export const ResourceOptions = memo(function ResourceOptions({
115115

116116
return (
117117
<div className={cn('border-[var(--border)] border-b py-2.5', search ? 'px-6' : 'px-4')}>
118-
<div className='flex items-center justify-between'>
118+
<div className='flex items-center'>
119119
{search && <SearchSection search={search} />}
120-
<div className='flex items-center'>
121-
{filterTags?.map((tag) => (
122-
<Chip key={tag.label} rightIcon={X} onClick={tag.onRemove}>
123-
{tag.label}
124-
</Chip>
125-
))}
126-
{isToggleFilter && filter.mode === 'toggle' ? (
127-
<Chip active={filter.active} leftIcon={ListFilter} onClick={filter.onToggle}>
128-
Filter
129-
</Chip>
130-
) : popoverFilter ? (
131-
<PopoverPrimitive.Root
132-
open={openMenu === 'filter'}
133-
onOpenChange={(open) =>
134-
setOpenMenu((current) => (open ? 'filter' : current === 'filter' ? null : current))
135-
}
136-
>
137-
<PopoverPrimitive.Anchor asChild>
138-
<div className='flex items-center'>
139-
<PopoverPrimitive.Trigger asChild>
140-
<Chip active={popoverFilter.active} leftIcon={ListFilter}>
141-
Filter
142-
</Chip>
143-
</PopoverPrimitive.Trigger>
144-
{sort && (
145-
<SortDropdown
146-
config={sort}
147-
open={openMenu === 'sort'}
148-
onOpenChange={(open) =>
149-
setOpenMenu((current) =>
150-
open ? 'sort' : current === 'sort' ? null : current
151-
)
152-
}
153-
/>
154-
)}
155-
</div>
156-
</PopoverPrimitive.Anchor>
157-
<PopoverPrimitive.Portal>
158-
<PopoverPrimitive.Content
159-
align='end'
160-
alignOffset={RESOURCE_MENU_EDGE_OFFSET}
161-
collisionPadding={6}
162-
sideOffset={6}
163-
className={cn(
164-
POPOVER_ANIMATION_CLASSES,
165-
'z-50 w-fit origin-[--radix-popover-content-transform-origin] rounded-xl border border-[var(--border)] bg-[var(--bg)] shadow-sm'
166-
)}
167-
>
168-
{popoverFilter.content}
169-
</PopoverPrimitive.Content>
170-
</PopoverPrimitive.Portal>
171-
</PopoverPrimitive.Root>
172-
) : null}
173-
{sort && (isToggleFilter || !popoverFilter) && <SortDropdown config={sort} />}
120+
<div className='ml-auto flex shrink-0 items-center gap-1.5'>
121+
{aside}
122+
<div className='flex items-center'>
123+
{filterTags?.map((tag) => (
124+
<Chip key={tag.label} rightIcon={X} onClick={tag.onRemove}>
125+
{tag.label}
126+
</Chip>
127+
))}
128+
{isToggleFilter && filter.mode === 'toggle' ? (
129+
<Chip active={filter.active} leftIcon={ListFilter} onClick={filter.onToggle}>
130+
Filter
131+
</Chip>
132+
) : popoverFilter ? (
133+
<PopoverPrimitive.Root
134+
open={openMenu === 'filter'}
135+
onOpenChange={(open) =>
136+
setOpenMenu((current) =>
137+
open ? 'filter' : current === 'filter' ? null : current
138+
)
139+
}
140+
>
141+
<PopoverPrimitive.Anchor asChild>
142+
<div className='flex items-center'>
143+
<PopoverPrimitive.Trigger asChild>
144+
<Chip active={popoverFilter.active} leftIcon={ListFilter}>
145+
Filter
146+
</Chip>
147+
</PopoverPrimitive.Trigger>
148+
{sort && (
149+
<SortDropdown
150+
config={sort}
151+
open={openMenu === 'sort'}
152+
onOpenChange={(open) =>
153+
setOpenMenu((current) =>
154+
open ? 'sort' : current === 'sort' ? null : current
155+
)
156+
}
157+
/>
158+
)}
159+
</div>
160+
</PopoverPrimitive.Anchor>
161+
<PopoverPrimitive.Portal>
162+
<PopoverPrimitive.Content
163+
align='end'
164+
alignOffset={RESOURCE_MENU_EDGE_OFFSET}
165+
collisionPadding={6}
166+
sideOffset={6}
167+
className={cn(
168+
POPOVER_ANIMATION_CLASSES,
169+
'z-50 w-fit origin-[--radix-popover-content-transform-origin] rounded-xl border border-[var(--border)] bg-[var(--bg)] shadow-sm'
170+
)}
171+
>
172+
{popoverFilter.content}
173+
</PopoverPrimitive.Content>
174+
</PopoverPrimitive.Portal>
175+
</PopoverPrimitive.Root>
176+
) : null}
177+
{sort && (isToggleFilter || !popoverFilter) && <SortDropdown config={sort} />}
178+
</div>
174179
</div>
175-
{aside && <div className='flex shrink-0 items-center gap-1.5'>{aside}</div>}
176180
</div>
177181
</div>
178182
)

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export function ConnectorsSection({
202202
No connected sources yet. Connect an external source to automatically sync documents.
203203
</p>
204204
) : (
205-
<div className='-mx-2 mt-2 flex flex-col gap-0.5'>
205+
<div className='mt-2 flex flex-col gap-0.5'>
206206
{connectors.map((connector) => (
207207
<ConnectorCard
208208
key={connector.id}

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,9 @@ export function Table({
640640
}
641641
/>
642642
)}
643-
{/* Sort + filter render in both modes (left-aligned). In embedded (mothership)
644-
mode there's no Resource.Header, so the run/stop control rides in the options
645-
bar's right-aligned `aside` slot — opposite the left-aligned filter/sort. */}
643+
{/* Sort + filter render in both modes. In embedded (mothership) mode there's no
644+
Resource.Header, so the run/stop control rides in the options bar's `aside`
645+
slot, just left of filter/sort. */}
646646
<Resource.Options
647647
sort={sortConfig}
648648
filter={filterConfig}

0 commit comments

Comments
 (0)