Skip to content

Commit 416b32f

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
refactor(ui): share collapsible workflow field cards
1 parent 76d44d6 commit 416b32f

6 files changed

Lines changed: 339 additions & 244 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx

Lines changed: 49 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { useMemo, useRef } from 'react'
44
import {
55
Badge,
66
Button,
7+
CollapsibleCard,
78
Combobox,
89
type ComboboxOption,
910
cn,
10-
handleKeyboardActivation,
1111
Input,
1212
Label,
13+
OverflowText,
1314
Trash,
1415
} from '@sim/emcn'
1516
import { Plus } from '@sim/emcn/icons'
@@ -235,55 +236,46 @@ export function DocumentTagEntry({
235236
}
236237

237238
/**
238-
* Renders the tag header with name, badge, and action buttons
239+
* Renders the tag summary with its type badge
239240
* Shows tag name only when collapsed (as summary), generic label when expanded
240241
*/
241-
const renderTagHeader = (tag: DocumentTag, index: number) => (
242-
<div
243-
role='group'
244-
aria-label={`Tag ${index + 1}`}
245-
className='flex cursor-pointer items-center justify-between rounded-t-[4px] bg-[var(--surface-4)] px-2.5 py-[5px]'
246-
onClick={() => toggleCollapse(tag.id)}
247-
onKeyDown={(event) => {
248-
if (event.target !== event.currentTarget) return
249-
handleKeyboardActivation(event, () => toggleCollapse(tag.id))
250-
}}
251-
>
252-
<div className='flex min-w-0 flex-1 items-center gap-2'>
253-
<span className='block truncate text-[var(--text-tertiary)] text-sm'>
254-
{tag.collapsed ? tag.tagName || `Tag ${index + 1}` : `Tag ${index + 1}`}
255-
</span>
256-
{tag.collapsed && tag.tagName && (
257-
<Badge variant='type' size='sm'>
258-
{FIELD_TYPE_LABELS[tag.fieldType] || 'Text'}
259-
</Badge>
260-
)}
261-
</div>
262-
<div
263-
role='presentation'
264-
className='flex items-center gap-2 pl-2'
265-
onClick={(e) => e.stopPropagation()}
242+
const renderTitle = (tag: DocumentTag, index: number) => (
243+
<span className='flex min-w-0 items-center gap-2'>
244+
<OverflowText
245+
label={tag.collapsed ? tag.tagName || `Tag ${index + 1}` : `Tag ${index + 1}`}
246+
focusTarget='nearest-interactive'
266247
>
267-
<Button
268-
variant='ghost'
269-
onClick={addTag}
270-
disabled={isReadOnly || !canAddMoreTags}
271-
className='h-auto p-0'
272-
>
273-
<Plus className='size-[14px]' />
274-
<span className='sr-only'>Add Tag</span>
275-
</Button>
276-
<Button
277-
variant='ghost-destructive'
278-
onClick={() => removeTag(tag.id)}
279-
disabled={isReadOnly}
280-
className='h-auto p-0'
281-
>
282-
<Trash className='size-[14px]' />
283-
<span className='sr-only'>Delete Tag</span>
284-
</Button>
285-
</div>
286-
</div>
248+
{tag.collapsed ? tag.tagName || `Tag ${index + 1}` : `Tag ${index + 1}`}
249+
</OverflowText>
250+
{tag.collapsed && tag.tagName && (
251+
<Badge variant='type' size='sm'>
252+
{FIELD_TYPE_LABELS[tag.fieldType] || 'Text'}
253+
</Badge>
254+
)}
255+
</span>
256+
)
257+
258+
const renderActions = (tag: DocumentTag) => (
259+
<>
260+
<Button
261+
variant='ghost'
262+
onClick={addTag}
263+
disabled={isReadOnly || !canAddMoreTags}
264+
className='h-auto p-0'
265+
>
266+
<Plus className='size-[14px]' />
267+
<span className='sr-only'>Add Tag</span>
268+
</Button>
269+
<Button
270+
variant='ghost-destructive'
271+
onClick={() => removeTag(tag.id)}
272+
disabled={isReadOnly}
273+
className='h-auto p-0'
274+
>
275+
<Trash className='size-[14px]' />
276+
<span className='sr-only'>Delete Tag</span>
277+
</Button>
278+
</>
287279
)
288280

289281
/**
@@ -385,7 +377,7 @@ export function DocumentTagEntry({
385377
}))
386378

387379
return (
388-
<div className='flex flex-col gap-2 rounded-b-[4px] border-[var(--border-1)] border-t bg-[var(--surface-2)] px-2.5 pt-1.5 pb-2.5'>
380+
<>
389381
<div className='flex flex-col gap-1.5'>
390382
<Label className='text-small'>Tag</Label>
391383
<Combobox
@@ -401,24 +393,25 @@ export function DocumentTagEntry({
401393
<Label className='text-small'>Value</Label>
402394
{renderValueInput(tag)}
403395
</div>
404-
</div>
396+
</>
405397
)
406398
}
407399

408400
return (
409401
<div className='space-y-2'>
410402
{tags.map((tag, index) => (
411-
<div
403+
<CollapsibleCard
412404
key={tag.id}
413405
data-tag-id={tag.id}
414-
className={cn(
415-
'rounded-sm border border-[var(--border-1)]',
416-
tag.collapsed ? 'overflow-hidden' : 'overflow-visible'
417-
)}
406+
role='group'
407+
aria-label={`Tag ${index + 1}`}
408+
title={renderTitle(tag, index)}
409+
actions={renderActions(tag)}
410+
collapsed={Boolean(tag.collapsed)}
411+
onToggleCollapse={() => toggleCollapse(tag.id)}
418412
>
419-
{renderTagHeader(tag, index)}
420413
{!tag.collapsed && renderTagContent(tag)}
421-
</div>
414+
</CollapsibleCard>
422415
))}
423416
</div>
424417
)

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx

Lines changed: 51 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { useRef } from 'react'
22
import {
33
Badge,
44
Button,
5+
CollapsibleCard,
56
Combobox,
67
type ComboboxOption,
78
cn,
8-
handleKeyboardActivation,
99
Input,
1010
Label,
11+
OverflowText,
1112
Trash,
1213
} from '@sim/emcn'
1314
import { Plus } from '@sim/emcn/icons'
@@ -102,56 +103,46 @@ export function FilterRuleRow({
102103
label,
103104
})
104105

105-
const renderHeader = () => (
106-
<div
107-
role='group'
108-
aria-label={`Condition ${index + 1}`}
109-
className='flex cursor-pointer items-center justify-between rounded-t-[4px] bg-[var(--surface-4)] px-2.5 py-[5px]'
110-
onClick={() => onToggleCollapse(rule.id)}
111-
onKeyDown={(event) => {
112-
if (event.target !== event.currentTarget) return
113-
handleKeyboardActivation(event, () => onToggleCollapse(rule.id))
114-
}}
115-
>
116-
<div className='flex min-w-0 flex-1 items-center gap-2'>
117-
<span className='block truncate text-[var(--text-tertiary)] text-sm'>
118-
{rule.collapsed && rule.column
119-
? formatDisplayText(getColumnLabel(rule.column), {
120-
workflowSearchHighlight: getLabelHighlight('column', getColumnLabel(rule.column)),
121-
})
122-
: `Condition ${index + 1}`}
123-
</span>
124-
{rule.collapsed && rule.column && (
125-
<Badge variant='type' size='sm'>
126-
{formatDisplayText(getOperatorLabel(rule.operator), {
127-
workflowSearchHighlight: getLabelHighlight(
128-
'operator',
129-
getOperatorLabel(rule.operator)
130-
),
131-
})}
132-
</Badge>
133-
)}
134-
</div>
135-
<div
136-
role='presentation'
137-
className='flex items-center gap-2 pl-2'
138-
onClick={(e) => e.stopPropagation()}
106+
const renderTitle = () => (
107+
<span className='flex min-w-0 items-center gap-2'>
108+
<OverflowText
109+
label={
110+
rule.collapsed && rule.column ? getColumnLabel(rule.column) : `Condition ${index + 1}`
111+
}
112+
focusTarget='nearest-interactive'
139113
>
140-
<Button variant='ghost' onClick={onAdd} disabled={isReadOnly} className='h-auto p-0'>
141-
<Plus className='size-[14px]' />
142-
<span className='sr-only'>Add Condition</span>
143-
</Button>
144-
<Button
145-
variant='ghost-destructive'
146-
onClick={() => onRemove(rule.id)}
147-
disabled={isReadOnly}
148-
className='h-auto p-0'
149-
>
150-
<Trash className='size-[14px]' />
151-
<span className='sr-only'>Delete Condition</span>
152-
</Button>
153-
</div>
154-
</div>
114+
{rule.collapsed && rule.column
115+
? formatDisplayText(getColumnLabel(rule.column), {
116+
workflowSearchHighlight: getLabelHighlight('column', getColumnLabel(rule.column)),
117+
})
118+
: `Condition ${index + 1}`}
119+
</OverflowText>
120+
{rule.collapsed && rule.column && (
121+
<Badge variant='type' size='sm'>
122+
{formatDisplayText(getOperatorLabel(rule.operator), {
123+
workflowSearchHighlight: getLabelHighlight('operator', getOperatorLabel(rule.operator)),
124+
})}
125+
</Badge>
126+
)}
127+
</span>
128+
)
129+
130+
const renderActions = () => (
131+
<>
132+
<Button variant='ghost' onClick={onAdd} disabled={isReadOnly} className='h-auto p-0'>
133+
<Plus className='size-[14px]' />
134+
<span className='sr-only'>Add Condition</span>
135+
</Button>
136+
<Button
137+
variant='ghost-destructive'
138+
onClick={() => onRemove(rule.id)}
139+
disabled={isReadOnly}
140+
className='h-auto p-0'
141+
>
142+
<Trash className='size-[14px]' />
143+
<span className='sr-only'>Delete Condition</span>
144+
</Button>
145+
</>
155146
)
156147

157148
const renderValueInput = () => (
@@ -209,7 +200,7 @@ export function FilterRuleRow({
209200
)
210201

211202
const renderContent = () => (
212-
<div className='flex flex-col gap-2 rounded-b-[4px] border-[var(--border-1)] border-t bg-[var(--surface-2)] px-2.5 pt-1.5 pb-2.5'>
203+
<>
213204
{index > 0 && (
214205
<div className='flex flex-col gap-1.5'>
215206
<Label className='text-small'>Logic</Label>
@@ -281,19 +272,20 @@ export function FilterRuleRow({
281272
<Label className='text-small'>Value</Label>
282273
{renderValueInput()}
283274
</div>
284-
</div>
275+
</>
285276
)
286277

287278
return (
288-
<div
279+
<CollapsibleCard
289280
data-filter-id={rule.id}
290-
className={cn(
291-
'rounded-sm border border-[var(--border-1)]',
292-
rule.collapsed ? 'overflow-hidden' : 'overflow-visible'
293-
)}
281+
role='group'
282+
aria-label={`Condition ${index + 1}`}
283+
title={renderTitle()}
284+
actions={renderActions()}
285+
collapsed={Boolean(rule.collapsed)}
286+
onToggleCollapse={() => onToggleCollapse(rule.id)}
294287
>
295-
{renderHeader()}
296288
{!rule.collapsed && renderContent()}
297-
</div>
289+
</CollapsibleCard>
298290
)
299291
}

0 commit comments

Comments
 (0)