Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import type { ComponentType } from 'react'
import {
BulkActionBar,
BulkActionButton,
cn,
DropdownMenu,
Expand Down Expand Up @@ -91,57 +92,48 @@ export function ResourceActionBar({
className
)}
>
<div className='flex items-center gap-2 rounded-[10px] border border-[var(--border)] bg-[var(--surface-2)] px-2 py-1.5'>
<span
className={cn(
'px-1 text-small',
exceedsLimit ? 'text-[var(--text-error)]' : 'text-[var(--text-secondary)]'
)}
>
{exceedsLimit
? `${selectedCount} selected · select ${maxSelectable} or fewer`
: `${selectedCount} selected`}
</span>
<div className='flex items-center gap-[5px]'>
{onDownload && (
<ActionButton
icon={Download}
label='Download'
onClick={onDownload}
disabled={actionsDisabled}
/>
)}
{onMove && moveOptions && (
<DropdownMenu>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<DropdownMenuTrigger asChild>
<BulkActionButton aria-label='Move' disabled={actionsDisabled}>
<Folder className='size-[12px]' />
</BulkActionButton>
</DropdownMenuTrigger>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Move</Tooltip.Content>
</Tooltip.Root>
<DropdownMenuContent
side='top'
align='center'
className='max-h-[240px] overflow-y-auto'
>
{renderMoveOptions(moveOptions, onMove)}
</DropdownMenuContent>
</DropdownMenu>
)}
{onDelete && (
<ActionButton
icon={Trash}
label='Delete'
onClick={onDelete}
disabled={actionsDisabled}
/>
)}
</div>
</div>
<BulkActionBar
label={
<span className={exceedsLimit ? 'text-[var(--text-error)]' : undefined}>
{exceedsLimit
? `${selectedCount} selected · select ${maxSelectable} or fewer`
: `${selectedCount} selected`}
</span>
}
>
{onDownload && (
<ActionButton
icon={Download}
label='Download'
onClick={onDownload}
disabled={actionsDisabled}
/>
)}
{onMove && moveOptions && (
<DropdownMenu>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<DropdownMenuTrigger asChild>
<BulkActionButton aria-label='Move' disabled={actionsDisabled}>
<Folder className='size-[12px]' />
</BulkActionButton>
</DropdownMenuTrigger>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Move</Tooltip.Content>
</Tooltip.Root>
<DropdownMenuContent
side='top'
align='center'
className='max-h-[240px] overflow-y-auto'
>
{renderMoveOptions(moveOptions, onMove)}
</DropdownMenuContent>
</DropdownMenu>
)}
{onDelete && (
<ActionButton icon={Trash} label='Delete' onClick={onDelete} disabled={actionsDisabled} />
)}
</BulkActionBar>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BulkActionButton, cn, Tooltip } from '@sim/emcn'
import { BulkActionBar, BulkActionButton, cn, Tooltip } from '@sim/emcn'
import { Ban, Circle, Trash } from '@sim/emcn/icons'
import { domAnimation, LazyMotion, m } from 'framer-motion'
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
Expand Down Expand Up @@ -53,70 +53,70 @@ export function ActionBar({
transition={{ duration: 0.2 }}
className={cn('-translate-x-1/2 fixed bottom-6 left-1/2 z-[var(--z-dropdown)]', className)}
>
<div className='flex items-center gap-2 rounded-[10px] border border-[var(--border)] bg-[var(--surface-2)] px-2 py-1.5'>
<span className='px-1 text-[var(--text-secondary)] text-small'>
{isAllSelected ? totalCount : selectedCount} selected
{showSelectAllOption && (
<>
{' · '}
<button
type='button'
onClick={onSelectAll}
className='text-[var(--brand-secondary)] hover-hover:underline'
>
Select all
</button>
</>
)}
{isAllSelected && onClearSelectAll && (
<>
{' · '}
<button
type='button'
onClick={onClearSelectAll}
className='text-[var(--brand-secondary)] hover-hover:underline'
>
Clear
</button>
</>
)}
</span>
<BulkActionBar
label={
<>
{isAllSelected ? totalCount : selectedCount} selected
{showSelectAllOption && (
<>
{' · '}
<button
type='button'
onClick={onSelectAll}
className='text-[var(--brand-secondary)] hover-hover:underline'
>
Select all
</button>
</>
)}
{isAllSelected && onClearSelectAll && (
<>
{' · '}
<button
type='button'
onClick={onClearSelectAll}
className='text-[var(--brand-secondary)] hover-hover:underline'
>
Clear
</button>
</>
)}
</>
}
>
{showEnableButton && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<BulkActionButton aria-label='Enable' onClick={onEnable} disabled={isLoading}>
<Circle className='size-[12px]' />
</BulkActionButton>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Enable</Tooltip.Content>
</Tooltip.Root>
)}

<div className='flex items-center gap-[5px]'>
{showEnableButton && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<BulkActionButton aria-label='Enable' onClick={onEnable} disabled={isLoading}>
<Circle className='size-[12px]' />
</BulkActionButton>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Enable</Tooltip.Content>
</Tooltip.Root>
)}
{showDisableButton && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<BulkActionButton aria-label='Disable' onClick={onDisable} disabled={isLoading}>
<Ban className='size-[12px]' />
</BulkActionButton>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Disable</Tooltip.Content>
</Tooltip.Root>
)}

{showDisableButton && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<BulkActionButton aria-label='Disable' onClick={onDisable} disabled={isLoading}>
<Ban className='size-[12px]' />
</BulkActionButton>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Disable</Tooltip.Content>
</Tooltip.Root>
)}

{onDelete && canEdit && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<BulkActionButton aria-label='Delete' onClick={onDelete} disabled={isLoading}>
<Trash className='size-[12px]' />
</BulkActionButton>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Delete</Tooltip.Content>
</Tooltip.Root>
)}
</div>
</div>
{onDelete && canEdit && (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<BulkActionButton aria-label='Delete' onClick={onDelete} disabled={isLoading}>
<Trash className='size-[12px]' />
</BulkActionButton>
</Tooltip.Trigger>
<Tooltip.Content side='top'>Delete</Tooltip.Content>
</Tooltip.Root>
)}
</BulkActionBar>
</m.div>
</LazyMotion>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import type React from 'react'
import { BulkActionButton, cn, Tooltip } from '@sim/emcn'
import { BulkActionBar, BulkActionButton, cn, Tooltip } from '@sim/emcn'
import { Eye, PlayOutline, RefreshCw, Square } from '@sim/emcn/icons'
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'

Expand Down Expand Up @@ -89,47 +89,42 @@ export function TableActionBar({
className
)}
>
<div className='pointer-events-auto flex items-center gap-2 rounded-[10px] border border-[var(--border)] bg-[var(--surface-2)] px-2 py-1.5'>
<span className='px-1 text-[var(--text-secondary)] text-small'>
{selectedCellCount === 1
<BulkActionBar
className='pointer-events-auto'
label={
selectedCellCount === 1
? 'Selected 1 workflow cell'
: `Selected ${selectedCellCount} workflow cells`}
</span>
: `Selected ${selectedCellCount} workflow cells`
}
>
{showPlay && (
<ActionIconButton label={playLabel} onClick={onPlay} disabled={isLoading}>
<PlayOutline className='size-[12px]' />
</ActionIconButton>
)}

<div className='flex items-center gap-[5px]'>
{showPlay && (
<ActionIconButton label={playLabel} onClick={onPlay} disabled={isLoading}>
<PlayOutline className='size-[12px]' />
</ActionIconButton>
)}
{showRefresh && (
<ActionIconButton label={refreshLabel} onClick={onRefresh} disabled={isLoading}>
<RefreshCw className='size-[12px]' />
</ActionIconButton>
)}

{showRefresh && (
<ActionIconButton label={refreshLabel} onClick={onRefresh} disabled={isLoading}>
<RefreshCw className='size-[12px]' />
</ActionIconButton>
)}
{runningCount > 0 && (
<ActionIconButton label={stopLabel} onClick={onStopWorkflows} disabled={isLoading}>
<Square className='size-[12px]' />
</ActionIconButton>
)}

{runningCount > 0 && (
<ActionIconButton
label={stopLabel}
onClick={onStopWorkflows}
disabled={isLoading}
>
<Square className='size-[12px]' />
</ActionIconButton>
)}

{onViewExecution && (
<ActionIconButton
label='View execution'
onClick={onViewExecution}
disabled={isLoading}
>
<Eye className='size-[12px]' />
</ActionIconButton>
)}
</div>
</div>
{onViewExecution && (
<ActionIconButton
label='View execution'
onClick={onViewExecution}
disabled={isLoading}
>
<Eye className='size-[12px]' />
</ActionIconButton>
)}
</BulkActionBar>
</m.div>
)}
</AnimatePresence>
Expand Down
31 changes: 31 additions & 0 deletions packages/emcn/src/components/bulk-action-bar/bulk-action-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'
import { cn } from '../../lib/cn'

export interface BulkActionBarProps extends HTMLAttributes<HTMLDivElement> {
/** Selection summary, including optional inline selection actions or limit warnings. */
label: ReactNode
children: ReactNode
}

/**
* Shared selection-action surface. Callers own visibility, positioning, animation
* and commands; compose actions with BulkActionButton.
* @example <BulkActionBar label='2 selected'><BulkActionButton aria-label='Delete'><Trash /></BulkActionButton></BulkActionBar>
*/
export const BulkActionBar = forwardRef<HTMLDivElement, BulkActionBarProps>(
({ label, children, className, ...props }, ref) => (
<div
{...props}
ref={ref}
className={cn(
'flex items-center gap-2 rounded-[10px] border border-[var(--border)] bg-[var(--surface-2)] px-2 py-1.5',
className
)}
>
<span className='px-1 text-[var(--text-secondary)] text-small'>{label}</span>
<div className='flex items-center gap-[5px]'>{children}</div>
</div>
)
)

BulkActionBar.displayName = 'BulkActionBar'
1 change: 1 addition & 0 deletions packages/emcn/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { Avatar, AvatarFallback, AvatarImage } from './avatar/avatar'
export { Badge, type BadgeProps } from './badge/badge'
export { Banner } from './banner/banner'
export { BulkActionBar, type BulkActionBarProps } from './bulk-action-bar/bulk-action-bar'
export {
BulkActionButton,
type BulkActionButtonProps,
Expand Down
Loading