mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
improvement: tables, dropdown
This commit is contained in:
@@ -123,6 +123,7 @@
|
||||
--brand-secondary: #33b4ff;
|
||||
--brand-tertiary: #22c55e;
|
||||
--brand-tertiary-2: #32bd7e;
|
||||
--selection: #1a5cf6;
|
||||
--warning: #ea580c;
|
||||
|
||||
/* Utility */
|
||||
@@ -245,6 +246,7 @@
|
||||
--brand-secondary: #33b4ff;
|
||||
--brand-tertiary: #22c55e;
|
||||
--brand-tertiary-2: #32bd7e;
|
||||
--selection: #4b83f7;
|
||||
--warning: #ff6600;
|
||||
|
||||
/* Utility */
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export type { ActiveFilter, ColumnOption, FilterConfig, SortConfig } from './resource-options-bar'
|
||||
export { ResourceOptionsBar } from './resource-options-bar'
|
||||
|
||||
@@ -1,25 +1,79 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { ArrowUpDown, Button, ListFilter, Search } from '@/components/emcn'
|
||||
import {
|
||||
ArrowDown,
|
||||
ArrowUp,
|
||||
ArrowUpDown,
|
||||
Button,
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
ListFilter,
|
||||
Search,
|
||||
} from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
type SortDirection = 'asc' | 'desc'
|
||||
|
||||
export interface ColumnOption {
|
||||
id: string
|
||||
label: string
|
||||
type?: string
|
||||
icon?: React.ElementType
|
||||
}
|
||||
|
||||
export interface SortConfig {
|
||||
options: ColumnOption[]
|
||||
active: { column: string; direction: SortDirection } | null
|
||||
onSort: (column: string, direction: SortDirection) => void
|
||||
onClear?: () => void
|
||||
}
|
||||
|
||||
export interface ActiveFilter {
|
||||
column: string
|
||||
operator: string
|
||||
}
|
||||
|
||||
export interface FilterConfig {
|
||||
options: ColumnOption[]
|
||||
active: ActiveFilter[]
|
||||
onToggle: (column: string, operator: string) => void
|
||||
onClear?: () => void
|
||||
}
|
||||
|
||||
const DEFAULT_FILTER_OPERATORS = [
|
||||
{ id: 'empty', label: 'Is empty' },
|
||||
{ id: 'not_empty', label: 'Is not empty' },
|
||||
] as const
|
||||
|
||||
const BOOLEAN_FILTER_OPERATORS = [
|
||||
{ id: 'eq_true', label: 'Is true' },
|
||||
{ id: 'eq_false', label: 'Is false' },
|
||||
] as const
|
||||
|
||||
interface ResourceOptionsBarProps {
|
||||
search?: {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
placeholder?: string
|
||||
}
|
||||
onSort?: () => void
|
||||
onFilter?: () => void
|
||||
sort?: SortConfig
|
||||
filter?: FilterConfig
|
||||
toolbarActions?: ReactNode
|
||||
}
|
||||
|
||||
export function ResourceOptionsBar({
|
||||
search,
|
||||
onSort,
|
||||
onFilter,
|
||||
sort,
|
||||
filter,
|
||||
toolbarActions,
|
||||
}: ResourceOptionsBarProps) {
|
||||
const hasContent = search || onSort || onFilter || toolbarActions
|
||||
const hasContent = search || sort || filter || toolbarActions
|
||||
if (!hasContent) return null
|
||||
|
||||
return (
|
||||
@@ -43,21 +97,114 @@ export function ResourceOptionsBar({
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center gap-[6px]'>
|
||||
{onFilter && (
|
||||
<Button variant='subtle' className='px-[8px] py-[4px] text-[12px]' onClick={onFilter}>
|
||||
<ListFilter className='mr-[6px] h-[14px] w-[14px] text-[var(--text-icon)]' />
|
||||
Filter
|
||||
</Button>
|
||||
)}
|
||||
{onSort && (
|
||||
<Button variant='subtle' className='px-[8px] py-[4px] text-[12px]' onClick={onSort}>
|
||||
<ArrowUpDown className='mr-[6px] h-[14px] w-[14px] text-[var(--text-icon)]' />
|
||||
Sort
|
||||
</Button>
|
||||
)}
|
||||
{filter && <FilterDropdown config={filter} />}
|
||||
{sort && <SortDropdown config={sort} />}
|
||||
{toolbarActions}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SortDropdown({ config }: { config: SortConfig }) {
|
||||
const { options, active, onSort, onClear } = config
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='subtle' className='px-[8px] py-[4px] text-[12px]'>
|
||||
<ArrowUpDown className='mr-[6px] h-[14px] w-[14px] text-[var(--text-icon)]' />
|
||||
Sort
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end'>
|
||||
{options.map((option) => {
|
||||
const isActive = active?.column === option.id
|
||||
const Icon = option.icon
|
||||
const DirectionIcon = isActive ? (active.direction === 'asc' ? ArrowUp : ArrowDown) : null
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={option.id}
|
||||
onSelect={() => {
|
||||
if (isActive) {
|
||||
onSort(option.id, active.direction === 'asc' ? 'desc' : 'asc')
|
||||
} else {
|
||||
onSort(option.id, 'desc')
|
||||
}
|
||||
}}
|
||||
>
|
||||
{Icon && <Icon />}
|
||||
{option.label}
|
||||
{DirectionIcon && (
|
||||
<DirectionIcon className='ml-auto h-[12px] w-[12px] text-[var(--text-tertiary)]' />
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
})}
|
||||
{active && onClear && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={onClear} className='text-[var(--text-tertiary)]'>
|
||||
Clear sort
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
function FilterDropdown({ config }: { config: FilterConfig }) {
|
||||
const { options, active, onToggle, onClear } = config
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='subtle' className='px-[8px] py-[4px] text-[12px]'>
|
||||
<ListFilter className='mr-[6px] h-[14px] w-[14px] text-[var(--text-icon)]' />
|
||||
Filter
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end'>
|
||||
{options.map((option) => {
|
||||
const operators =
|
||||
option.type === 'boolean' ? BOOLEAN_FILTER_OPERATORS : DEFAULT_FILTER_OPERATORS
|
||||
const activeFilter = active.find((f) => f.column === option.id)
|
||||
const Icon = option.icon
|
||||
|
||||
return (
|
||||
<DropdownMenuSub key={option.id}>
|
||||
<DropdownMenuSubTrigger>
|
||||
{Icon && <Icon />}
|
||||
{option.label}
|
||||
{activeFilter && (
|
||||
<span className='ml-auto h-[6px] w-[6px] rounded-full bg-[var(--text-tertiary)]' />
|
||||
)}
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
{operators.map((op) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={op.id}
|
||||
checked={activeFilter?.operator === op.id}
|
||||
onCheckedChange={() => onToggle(option.id, op.id)}
|
||||
>
|
||||
{op.label}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
)
|
||||
})}
|
||||
{active.length > 0 && onClear && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onSelect={onClear} className='text-[var(--text-tertiary)]'>
|
||||
Clear all filters
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { ArrowDown, ArrowUp, Button, Plus, Skeleton } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { ResourceHeader } from './components/resource-header'
|
||||
import type { SortConfig } from './components/resource-options-bar'
|
||||
import { ResourceOptionsBar } from './components/resource-options-bar'
|
||||
|
||||
export interface ResourceColumn {
|
||||
@@ -37,8 +38,6 @@ interface ResourceProps {
|
||||
placeholder?: string
|
||||
}
|
||||
defaultSort: string
|
||||
onSort?: () => void
|
||||
onFilter?: () => void
|
||||
toolbarActions?: ReactNode
|
||||
columns: ResourceColumn[]
|
||||
rows: ResourceRow[]
|
||||
@@ -61,8 +60,6 @@ export function Resource({
|
||||
create,
|
||||
search,
|
||||
defaultSort,
|
||||
onSort,
|
||||
onFilter,
|
||||
toolbarActions,
|
||||
columns,
|
||||
rows,
|
||||
@@ -84,13 +81,19 @@ export function Resource({
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleColumnSort = useCallback((columnId: string) => {
|
||||
setSort((prev) => {
|
||||
if (prev.column !== columnId) return { column: columnId, direction: 'desc' }
|
||||
return { column: columnId, direction: prev.direction === 'desc' ? 'asc' : 'desc' }
|
||||
})
|
||||
const handleSort = useCallback((column: string, direction: 'asc' | 'desc') => {
|
||||
setSort({ column, direction })
|
||||
}, [])
|
||||
|
||||
const sortConfig = useMemo<SortConfig>(
|
||||
() => ({
|
||||
options: columns.map((col) => ({ id: col.id, label: col.header })),
|
||||
active: sort,
|
||||
onSort: handleSort,
|
||||
}),
|
||||
[columns, sort, handleSort]
|
||||
)
|
||||
|
||||
const sortedRows = useMemo(() => {
|
||||
return [...rows].sort((a, b) => {
|
||||
const col = sort.column
|
||||
@@ -110,12 +113,7 @@ export function Resource({
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<ResourceHeader icon={icon} title={title} create={create} />
|
||||
<ResourceOptionsBar
|
||||
search={search}
|
||||
onSort={onSort}
|
||||
onFilter={onFilter}
|
||||
toolbarActions={toolbarActions}
|
||||
/>
|
||||
<ResourceOptionsBar search={search} sort={sortConfig} toolbarActions={toolbarActions} />
|
||||
|
||||
{isLoading ? (
|
||||
<DataTableSkeleton columns={columns} rowCount={loadingRows} />
|
||||
@@ -127,16 +125,22 @@ export function Resource({
|
||||
<thead className='shadow-[inset_0_-1px_0_var(--border)]'>
|
||||
<tr>
|
||||
{columns.map((col) => {
|
||||
const isActive = sort.column === col.id
|
||||
const SortIcon = sort.direction === 'asc' ? ArrowUp : ArrowDown
|
||||
return (
|
||||
<th key={col.id} className='h-10 px-[16px] py-[6px] text-left align-middle'>
|
||||
<Button
|
||||
variant='subtle'
|
||||
className='px-[8px] py-[4px] font-base text-[var(--text-muted)] hover:text-[var(--text-muted)]'
|
||||
onClick={() => handleColumnSort(col.id)}
|
||||
onClick={() =>
|
||||
handleSort(
|
||||
col.id,
|
||||
isActive ? (sort.direction === 'desc' ? 'asc' : 'desc') : 'desc'
|
||||
)
|
||||
}
|
||||
>
|
||||
{col.header}
|
||||
{sort.column === col.id && (
|
||||
{isActive && (
|
||||
<SortIcon className='ml-[4px] h-[12px] w-[12px] text-[var(--text-icon)]' />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -204,8 +204,6 @@ export function Knowledge() {
|
||||
placeholder: 'Search knowledge bases...',
|
||||
}}
|
||||
defaultSort='created'
|
||||
onSort={() => {}}
|
||||
onFilter={() => {}}
|
||||
columns={COLUMNS}
|
||||
rows={rows}
|
||||
onRowClick={handleRowClick}
|
||||
|
||||
@@ -189,8 +189,6 @@ export function Schedules() {
|
||||
placeholder: 'Search schedules...',
|
||||
}}
|
||||
defaultSort='nextRun'
|
||||
onSort={() => {}}
|
||||
onFilter={() => {}}
|
||||
columns={COLUMNS}
|
||||
rows={rows}
|
||||
onRowClick={handleRowClick}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -177,8 +177,6 @@ export function Tables() {
|
||||
placeholder: 'Search tables...',
|
||||
}}
|
||||
defaultSort='created'
|
||||
onSort={() => {}}
|
||||
onFilter={() => {}}
|
||||
columns={COLUMNS}
|
||||
rows={rows}
|
||||
onRowClick={handleRowClick}
|
||||
|
||||
@@ -49,7 +49,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default select-none items-center gap-[8px] rounded-[6px] px-[6px] py-[4px] text-[13px] text-[var(--text-primary)] outline-none transition-colors focus:bg-[var(--border-1)] data-[state=open]:bg-[var(--border-1)] [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0',
|
||||
'flex cursor-default select-none items-center gap-[8px] rounded-[5px] px-[8px] py-[5px] font-medium text-[12px] text-[var(--text-secondary)] outline-none transition-colors focus:bg-[var(--surface-4)] focus:text-[var(--text-primary)] data-[state=open]:bg-[var(--surface-4)] data-[state=open]:text-[var(--text-primary)] [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0',
|
||||
inset && 'pl-[28px]',
|
||||
className
|
||||
)}
|
||||
@@ -69,7 +69,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
ref={ref}
|
||||
className={cn(
|
||||
ANIMATION_CLASSES,
|
||||
'z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-hidden rounded-[6px] border border-[var(--border)] bg-white p-[6px] text-[var(--text-primary)] shadow-lg dark:bg-[var(--bg)]',
|
||||
'z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-hidden rounded-[8px] border border-[var(--border)] bg-white p-[6px] text-[var(--text-secondary)] shadow-sm dark:bg-[var(--bg)]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -87,7 +87,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
ANIMATION_CLASSES,
|
||||
'z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-[6px] border border-[var(--border)] bg-white p-[6px] text-[var(--text-primary)] shadow-md dark:bg-[var(--bg)]',
|
||||
'z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-[8px] border border-[var(--border)] bg-white p-[6px] text-[var(--text-secondary)] shadow-sm dark:bg-[var(--bg)]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -105,7 +105,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center gap-[8px] rounded-[6px] px-[6px] py-[4px] text-[13px] text-[var(--text-primary)] outline-none transition-colors focus:bg-[var(--border-1)] focus:text-[var(--text-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0',
|
||||
'relative flex cursor-default select-none items-center gap-[8px] rounded-[5px] px-[8px] py-[5px] font-medium text-[12px] text-[var(--text-secondary)] outline-none transition-colors focus:bg-[var(--surface-4)] focus:text-[var(--text-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0',
|
||||
inset && 'pl-[28px]',
|
||||
className
|
||||
)}
|
||||
@@ -121,13 +121,13 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-[6px] py-[4px] pr-[6px] pl-[28px] text-[13px] text-[var(--text-primary)] outline-none transition-colors focus:bg-[var(--border-1)] focus:text-[var(--text-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-[5px] py-[5px] pr-[8px] pl-[28px] font-medium text-[12px] text-[var(--text-secondary)] outline-none transition-colors focus:bg-[var(--surface-4)] focus:text-[var(--text-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className='absolute left-[6px] flex h-[14px] w-[14px] items-center justify-center'>
|
||||
<span className='absolute left-[8px] flex h-[14px] w-[14px] items-center justify-center'>
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className='h-[14px] w-[14px]' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
@@ -144,12 +144,12 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-[6px] py-[4px] pr-[6px] pl-[28px] text-[13px] text-[var(--text-primary)] outline-none transition-colors focus:bg-[var(--border-1)] focus:text-[var(--text-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
'relative flex cursor-default select-none items-center rounded-[5px] py-[5px] pr-[8px] pl-[28px] font-medium text-[12px] text-[var(--text-secondary)] outline-none transition-colors focus:bg-[var(--surface-4)] focus:text-[var(--text-primary)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className='absolute left-[6px] flex h-[14px] w-[14px] items-center justify-center'>
|
||||
<span className='absolute left-[8px] flex h-[14px] w-[14px] items-center justify-center'>
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className='h-[6px] w-[6px] fill-current' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
@@ -168,7 +168,7 @@ const DropdownMenuLabel = React.forwardRef<
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'px-[6px] py-[4px] font-medium text-[11px] text-[var(--text-tertiary)]',
|
||||
'px-[8px] py-[5px] font-medium text-[11px] text-[var(--text-tertiary)]',
|
||||
inset && 'pl-[28px]',
|
||||
className
|
||||
)}
|
||||
|
||||
25
apps/sim/components/emcn/icons/arrow-left.tsx
Normal file
25
apps/sim/components/emcn/icons/arrow-left.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { SVGProps } from 'react'
|
||||
|
||||
/**
|
||||
* ArrowLeft icon component
|
||||
* @param props - SVG properties including className, fill, etc.
|
||||
*/
|
||||
export function ArrowLeft(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<path d='M9.25 4L3 10.25L9.25 16.5' />
|
||||
<path d='M3 10.25H17.5' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
25
apps/sim/components/emcn/icons/arrow-right.tsx
Normal file
25
apps/sim/components/emcn/icons/arrow-right.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { SVGProps } from 'react'
|
||||
|
||||
/**
|
||||
* ArrowRight icon component
|
||||
* @param props - SVG properties including className, fill, etc.
|
||||
*/
|
||||
export function ArrowRight(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<path d='M11.25 4L17.5 10.25L11.25 16.5' />
|
||||
<path d='M17.5 10.25H3' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
26
apps/sim/components/emcn/icons/asterisk.tsx
Normal file
26
apps/sim/components/emcn/icons/asterisk.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { SVGProps } from 'react'
|
||||
|
||||
/**
|
||||
* Asterisk icon component - required field indicator
|
||||
* @param props - SVG properties including className, fill, etc.
|
||||
*/
|
||||
export function Asterisk(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<path d='M10.25 3V17.5' />
|
||||
<path d='M4 6.625L16.5 13.875' />
|
||||
<path d='M4 13.875L16.5 6.625' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
export { ArrowDown } from './arrow-down'
|
||||
export { ArrowLeft } from './arrow-left'
|
||||
export { ArrowRight } from './arrow-right'
|
||||
export { ArrowUp } from './arrow-up'
|
||||
export { ArrowUpDown } from './arrow-up-down'
|
||||
export { Asterisk } from './asterisk'
|
||||
export { Blimp } from './blimp'
|
||||
export { BubbleChatClose } from './bubble-chat-close'
|
||||
export { BubbleChatPreview } from './bubble-chat-preview'
|
||||
@@ -29,6 +32,7 @@ export { Loader } from './loader'
|
||||
export { MoreHorizontal } from './more-horizontal'
|
||||
export { NoWrap } from './no-wrap'
|
||||
export { PanelLeft } from './panel-left'
|
||||
export { Pencil } from './pencil'
|
||||
export { Play, PlayOutline } from './play'
|
||||
export { Plus } from './plus'
|
||||
export { Redo } from './redo'
|
||||
|
||||
@@ -7,17 +7,21 @@ import type { SVGProps } from 'react'
|
||||
export function Key(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width='16'
|
||||
height='8'
|
||||
viewBox='-0.5 -0.5 17 9'
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d='M7.92 3.20645H16V6.41206C16 6.84919 15.64 7.20947 15.2 7.20947C14.76 7.20947 14.4 6.84919 14.4 6.41206V4.80765H12.8V6.40886C12.8 6.84919 12.44 7.20947 12 7.20947C11.56 7.20947 11.2 6.84919 11.2 6.40886V4.80765H7.92C7.71926 5.77747 7.16654 6.63843 6.36859 7.22427C5.57064 7.81011 4.58407 8.07925 3.59943 7.97972C2.61479 7.88018 1.70193 7.41903 1.03716 6.68534C0.372398 5.95164 0.00288855 4.99745 2.02229e-06 4.00705C-0.000994899 3.01434 0.366627 2.05667 1.0315 1.31996C1.69637 0.583243 2.61107 0.120042 3.598 0.0202756C4.58494 -0.0794905 5.57372 0.191296 6.37238 0.780068C7.17104 1.36884 7.72261 2.23359 7.92 3.20645ZM4 6.40886C4.63652 6.40886 5.24697 6.15582 5.69706 5.70539C6.14715 5.25496 6.4 4.64405 6.4 4.00705C6.4 3.37005 6.14715 2.75914 5.69706 2.30871C5.24697 1.85829 4.63652 1.60524 4 1.60524C3.36348 1.60524 2.75303 1.85829 2.30295 2.30871C1.85286 2.75914 1.6 3.37005 1.6 4.00705C1.6 4.64405 1.85286 5.25496 2.30295 5.70539C2.75303 6.15582 3.36348 6.40886 4 6.40886Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
<circle cx='6.75' cy='10.25' r='3.5' />
|
||||
<path d='M10.25 10.25H19' />
|
||||
<path d='M14.5 10.25V13.5' />
|
||||
<path d='M17.5 10.25V12.5' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
25
apps/sim/components/emcn/icons/pencil.tsx
Normal file
25
apps/sim/components/emcn/icons/pencil.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { SVGProps } from 'react'
|
||||
|
||||
/**
|
||||
* Pencil icon component - edit/rename indicator
|
||||
* @param props - SVG properties including className, fill, etc.
|
||||
*/
|
||||
export function Pencil(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
{...props}
|
||||
>
|
||||
<path d='M14.75 1.25L19.25 5.75L7.25 17.75H2.75V13.25L14.75 1.25Z' />
|
||||
<path d='M12 4L16.5 8.5' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export function TypeBoolean(props: SVGProps<SVGSVGElement>) {
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
viewBox='-1.75 -1.5 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
|
||||
@@ -9,7 +9,7 @@ export function TypeJson(props: SVGProps<SVGSVGElement>) {
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
viewBox='-1.75 -1.75 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
|
||||
@@ -9,7 +9,7 @@ export function TypeNumber(props: SVGProps<SVGSVGElement>) {
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
viewBox='-1.75 -1.5 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
|
||||
@@ -9,7 +9,7 @@ export function TypeText(props: SVGProps<SVGSVGElement>) {
|
||||
<svg
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='-1 -2 24 24'
|
||||
viewBox='-1.75 -1.5 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.75'
|
||||
|
||||
@@ -339,8 +339,8 @@ export function useUpdateTableRow({ workspaceId, tableId }: RowMutationContext)
|
||||
|
||||
return res.json()
|
||||
},
|
||||
onMutate: async ({ rowId, data }) => {
|
||||
await queryClient.cancelQueries({ queryKey: tableKeys.rowsRoot(tableId) })
|
||||
onMutate: ({ rowId, data }) => {
|
||||
void queryClient.cancelQueries({ queryKey: tableKeys.rowsRoot(tableId) })
|
||||
|
||||
const previousQueries = queryClient.getQueriesData<TableRowsResponse>({
|
||||
queryKey: tableKeys.rowsRoot(tableId),
|
||||
|
||||
Reference in New Issue
Block a user