Are you sure you want to delete this pipeline? This action cannot be undone.
-
diff --git a/packages/ui/src/components/chat_deprecated.tsx b/packages/ui/src/components/chat_deprecated.tsx
index 44e3405fa1..d364bc519d 100644
--- a/packages/ui/src/components/chat_deprecated.tsx
+++ b/packages/ui/src/components/chat_deprecated.tsx
@@ -43,7 +43,7 @@ const Message: React.FC = ({ self, time, avatar, actions, children
>
{children}
diff --git a/packages/ui/src/components/checkbox.tsx b/packages/ui/src/components/checkbox.tsx
index 42368c6f5f..18f5f9fbf5 100644
--- a/packages/ui/src/components/checkbox.tsx
+++ b/packages/ui/src/components/checkbox.tsx
@@ -37,9 +37,9 @@ const Checkbox = forwardRef, Omit
{props.checked === 'indeterminate' ? (
-
+
) : (
-
+
)}
diff --git a/packages/ui/src/components/commit-copy-actions.tsx b/packages/ui/src/components/commit-copy-actions.tsx
index 18e3d2f7ce..7c30aaf7fa 100644
--- a/packages/ui/src/components/commit-copy-actions.tsx
+++ b/packages/ui/src/components/commit-copy-actions.tsx
@@ -1,7 +1,6 @@
import { KeyboardEvent } from 'react'
-import { ButtonGroup, ButtonGroupButtonProps, ButtonProps, Text, useCopyButton } from '@/components'
-import { useRouterContext } from '@/context'
+import { ButtonGroup, ButtonGroupButtonProps, ButtonProps, Link, Text, useCopyButton } from '@/components'
interface CommitCopyActionsProps {
sha: string
@@ -11,11 +10,10 @@ interface CommitCopyActionsProps {
export const CommitCopyActions = ({ sha, toCommitDetails, size = 'xs' }: CommitCopyActionsProps) => {
const { copyButtonProps, CopyIcon } = useCopyButton({ copyData: sha, iconSize: '2xs', color: 'surfaceGray' })
- const { navigate } = useRouterContext()
const handleNavigation = (ev: React.MouseEvent | React.KeyboardEvent) => {
ev.stopPropagation()
- navigate(toCommitDetails?.({ sha: sha || '' }) || '')
+ toCommitDetails?.({ sha: sha || '' })
}
const handleKeyDown = (e: KeyboardEvent) => {
@@ -28,11 +26,12 @@ export const CommitCopyActions = ({ sha, toCommitDetails, size = 'xs' }: CommitC
buttonsProps={[
{
children: (
-
- {sha.substring(0, 6)}
-
+
+
+ {sha.substring(0, 6)}
+
+
),
- onClick: handleNavigation,
onKeyDown: handleKeyDown,
className: 'font-mono'
},
diff --git a/packages/ui/src/components/commit-suggestions-dialog/commit-suggestions-dialog.tsx b/packages/ui/src/components/commit-suggestions-dialog/commit-suggestions-dialog.tsx
index 6828187fb3..7af25b4388 100644
--- a/packages/ui/src/components/commit-suggestions-dialog/commit-suggestions-dialog.tsx
+++ b/packages/ui/src/components/commit-suggestions-dialog/commit-suggestions-dialog.tsx
@@ -1,7 +1,7 @@
-import { FC } from 'react'
+import { FC, useEffect } from 'react'
import { useForm } from 'react-hook-form'
-import { Button, ButtonLayout, Dialog, FormInput, FormWrapper } from '@/components'
+import { Alert, Button, ButtonLayout, Dialog, FormInput, FormWrapper } from '@/components'
import { UsererrorError } from '@/types'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
@@ -32,6 +32,7 @@ export const CommitSuggestionsDialog: FC = ({
isOpen,
onClose,
commitTitlePlaceHolder,
+ error,
onFormSubmit,
isSubmitting
}) => {
@@ -44,7 +45,14 @@ export const CommitSuggestionsDialog: FC = ({
}
})
- const { register, handleSubmit } = formMethods
+ const { register, handleSubmit, reset } = formMethods
+ // Reset form when dialog opens or closes
+ useEffect(() => {
+ reset({
+ message: '',
+ title: commitTitlePlaceHolder
+ })
+ }, [isOpen, reset, commitTitlePlaceHolder])
return (
@@ -53,8 +61,8 @@ export const CommitSuggestionsDialog: FC = ({
Commit Changes
-
-
+
+
= ({
label="Extended description"
className="h-44"
/>
-
-
-
-
- Cancel
-
-
- {isSubmitting ? 'Committing...' : 'Commit changes'}
-
-
-
-
+ {error && (
+
+
+ {error.message || 'An error occurred while applying suggestions. Please try again.'}
+
+
+ )}
+
+
+
+
+
+
+ Cancel
+
+
+ {isSubmitting ? 'Committing...' : 'Commit changes'}
+
+
+
)
diff --git a/packages/ui/src/components/copy-button/copy-button.tsx b/packages/ui/src/components/copy-button/copy-button.tsx
index e82d9a2860..8cc17cf0bb 100644
--- a/packages/ui/src/components/copy-button/copy-button.tsx
+++ b/packages/ui/src/components/copy-button/copy-button.tsx
@@ -1,4 +1,4 @@
-import { FC } from 'react'
+import { forwardRef } from 'react'
import { Button, ButtonSizes, useCopyButton, UseCopyButtonProps, type ButtonVariants } from '@/components'
@@ -10,29 +10,26 @@ export interface CopyButtonProps extends Omit {
iconOnly?: boolean
}
-export const CopyButton: FC = ({
- name,
- className,
- buttonVariant = 'outline',
- iconSize = 'sm',
- size = 'sm',
- onClick,
- color,
- iconOnly = false
-}) => {
- const { copyButtonProps, CopyIcon } = useCopyButton({ onClick, copyData: name, iconSize, color })
+export const CopyButton = forwardRef(
+ (
+ { name, className, buttonVariant = 'outline', iconSize = 'sm', size = 'sm', onClick, color, iconOnly = false },
+ ref
+ ) => {
+ const { copyButtonProps, CopyIcon } = useCopyButton({ onClick, copyData: name, iconSize, color })
- return (
-
- {CopyIcon}
-
- )
-}
+ return (
+
+ {CopyIcon}
+
+ )
+ }
+)
CopyButton.displayName = 'CopyButton'
diff --git a/packages/ui/src/components/copy-tag.tsx b/packages/ui/src/components/copy-tag.tsx
new file mode 100644
index 0000000000..11005d22f0
--- /dev/null
+++ b/packages/ui/src/components/copy-tag.tsx
@@ -0,0 +1,36 @@
+import { useCallback, useEffect, useState } from 'react'
+
+import copy from 'clipboard-copy'
+
+import { Tag, TagProps } from './tag'
+
+type CopyTagProps = TagProps & {
+ actionIcon?: never
+ onActionClick?: never
+}
+
+export function CopyTag(props: CopyTagProps) {
+ const { value } = props
+
+ const [copied, setCopied] = useState(false)
+
+ useEffect(() => {
+ let timeoutId: number
+
+ if (copied) {
+ timeoutId = window.setTimeout(() => setCopied(false), 1000)
+ }
+
+ return () => {
+ clearTimeout(timeoutId)
+ }
+ }, [copied])
+
+ const handleCopy = useCallback(() => {
+ copy(value).then(() => {
+ setCopied(true)
+ })
+ }, [value])
+
+ return
+}
diff --git a/packages/ui/src/components/counter-badge.tsx b/packages/ui/src/components/counter-badge.tsx
index 6412156336..4c7ef5905e 100644
--- a/packages/ui/src/components/counter-badge.tsx
+++ b/packages/ui/src/components/counter-badge.tsx
@@ -1,8 +1,14 @@
+import { forwardRef } from 'react'
+
import { cn } from '@utils/cn'
import { cva, type VariantProps } from 'class-variance-authority'
-const counterBadgeVariants = cva('cn-badge cn-badge-counter cn-badge-surface inline-flex w-fit items-center', {
+const counterBadgeVariants = cva('cn-badge cn-badge-counter inline-flex w-fit items-center', {
variants: {
+ variant: {
+ outline: 'cn-badge-surface',
+ secondary: 'cn-badge-soft'
+ },
theme: {
default: 'cn-badge-muted',
info: 'cn-badge-info',
@@ -11,6 +17,7 @@ const counterBadgeVariants = cva('cn-badge cn-badge-counter cn-badge-surface inl
}
},
defaultVariants: {
+ variant: 'outline',
theme: 'default'
}
})
@@ -19,24 +26,29 @@ type CounterBadgeProps = Omit<
React.HTMLAttributes,
'color' | 'role' | 'aria-readonly' | 'tabIndex' | 'onClick'
> & {
+ variant?: VariantProps['variant']
theme?: VariantProps['theme']
}
-function CounterBadge({ className, theme = 'default', children, ...props }: CounterBadgeProps) {
- return (
-
- {children}
-
- )
-}
+const CounterBadge = forwardRef(
+ ({ className, variant, theme = 'default', children, ...props }, ref) => {
+ return (
+
+ {children}
+
+ )
+ }
+)
CounterBadge.displayName = 'CounterBadge'
export { CounterBadge, counterBadgeVariants }
diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx
index f8ab45cad3..741012c2e3 100644
--- a/packages/ui/src/components/dialog.tsx
+++ b/packages/ui/src/components/dialog.tsx
@@ -55,17 +55,26 @@ const Content = forwardRef(
return (
-
-
- {!hideClose && (
-
-
-
-
-
- )}
- {children}
-
+ {/* !!! */}
+ {/* For the scroll to work when using the dialog in Shadow DOM, the Overlay needs to wrap the Content */}
+ {/* Here’s the issue for the scroll bug in Shadow DOM - https://github.com/radix-ui/primitives/issues/3353 */}
+
+ event.preventDefault()}
+ {...props}
+ >
+ {!hideClose && (
+
+
+
+
+
+ )}
+ {children}
+
+
)
}
@@ -78,53 +87,60 @@ interface HeaderProps extends HTMLAttributes {
theme?: 'default' | 'warning' | 'danger'
}
-const Header = ({ className, icon, logo, theme = 'default', children, ...props }: HeaderProps) => {
- if (icon && logo) {
- console.warn('Dialog.Header: Cannot use both icon and logo props together')
- return null
- }
-
- // Find the title and description from children
- let title: React.ReactNode = null
- let description: React.ReactNode = null
+const Header = forwardRef(
+ ({ className, icon, logo, theme = 'default', children, ...props }, ref) => {
+ if (icon && logo) {
+ console.warn('Dialog.Header: Cannot use both icon and logo props together')
+ return null
+ }
- Children.forEach(children, child => {
- if (isValidElement(child)) {
- if (child.type === Title) {
- title = child
- } else if (child.type === Description) {
- description = child
+ // Find the title and description from children
+ let title: React.ReactNode = null
+ let description: React.ReactNode = null
+
+ Children.forEach(children, child => {
+ if (isValidElement(child)) {
+ if (child.type === Title) {
+ title = child
+ } else if (child.type === Description) {
+ description = child
+ }
}
- }
- })
-
- return (
-
-
- {icon && (
-
-
-
- )}
- {logo && (
-
-
-
- )}
- {title}
-
- {description}
-
- )
-}
+ })
-const Title = ({ className, ...props }: HTMLAttributes) => (
-
+ return (
+
+
+ {icon && (
+
+
+
+ )}
+ {logo && (
+
+
+
+ )}
+ {title}
+
+ {description}
+
+ )
+ }
)
+Header.displayName = 'Dialog.Header'
+
+const Title = forwardRef>(({ className, ...props }, ref) => (
+
+))
+Title.displayName = 'Dialog.Title'
-const Description = ({ className, ...props }: HTMLAttributes) => (
-
+const Description = forwardRef>(
+ ({ className, ...props }, ref) => (
+
+ )
)
+Description.displayName = 'Dialog.Description'
interface BodyProps {
className?: string
@@ -132,19 +148,22 @@ interface BodyProps {
children: ReactNode
}
-const Body = ({ className, classNameContent, children, ...props }: BodyProps) => (
+const Body = forwardRef(({ className, classNameContent, children, ...props }, ref) => (
{children}
-)
+))
+Body.displayName = 'Dialog.Body'
-const Footer = ({ className, ...props }: HTMLAttributes) => (
-
-)
+const Footer = forwardRef>(({ className, ...props }, ref) => (
+
+))
+Footer.displayName = 'Dialog.Footer'
const Close = forwardRef(({ children, className, ...props }, ref) => (
diff --git a/packages/ui/src/components/dialogs/delete-alert-dialog.tsx b/packages/ui/src/components/dialogs/delete-alert-dialog.tsx
index cf88f3ff71..a9e1b0dae3 100644
--- a/packages/ui/src/components/dialogs/delete-alert-dialog.tsx
+++ b/packages/ui/src/components/dialogs/delete-alert-dialog.tsx
@@ -1,6 +1,6 @@
import { ChangeEvent, FC, useMemo, useState } from 'react'
-import { Alert, AlertDialog, Fieldset, Input } from '@/components'
+import { Alert, AlertDialog, Message, MessageTheme, Text, TextInput } from '@/components'
import { useTranslation } from '@/context'
import { getErrorMessage } from '@utils/utils'
@@ -14,7 +14,10 @@ export interface DeleteAlertDialogProps {
error?: { type?: string; message: string } | null
withForm?: boolean
message?: string
+ deletionItemName?: string
deletionKeyword?: string
+ violation?: boolean
+ bypassable?: boolean
}
export const DeleteAlertDialog: FC = ({
@@ -27,59 +30,122 @@ export const DeleteAlertDialog: FC = ({
error,
withForm = false,
message,
- deletionKeyword = 'DELETE'
+ deletionItemName,
+ deletionKeyword = 'DELETE',
+ violation = false,
+ bypassable = false
}) => {
const { t } = useTranslation()
const [verification, setVerification] = useState('')
+ const [validationError, setValidationError] = useState(false)
const handleChangeVerification = (event: ChangeEvent) => {
setVerification(event.target.value)
+ if (validationError) {
+ setValidationError(false)
+ }
}
- const isDisabled = isLoading || (withForm && verification !== deletionKeyword)
-
const handleDelete = () => {
- if (isDisabled) return
+ if (withForm && verification !== deletionKeyword) {
+ setValidationError(true)
+ }
+
+ if (isLoading || (withForm && verification !== deletionKeyword)) return
+ setValidationError(false)
deleteFn(identifier!)
}
+ const handleOpenChange = () => {
+ setTimeout(() => {
+ setVerification('')
+ setValidationError(false)
+ }, 300)
+ onClose()
+ }
+
const displayMessageContent = useMemo(() => {
if (message) return message
+ const replaceText = '__IDENTIFIER__'
+
if (type) {
- return t(
+ const text = t(
'component:deleteDialog.descriptionWithType',
- `This will permanently delete your ${type} and remove all data. This action cannot be undone.`,
- { type: type }
+ `This will permanently delete your ${type} ${replaceText} and remove all data. This action cannot be undone.`,
+ { type: type, identifier: replaceText }
+ )
+
+ const parts = text.split(replaceText)
+
+ return (
+ <>
+ {parts[0]}
+ {(deletionItemName ?? identifier) && (
+
+ {deletionItemName ?? identifier}
+
+ )}
+ {parts[1]}
+ >
)
}
+
return t(
'component:deleteDialog.description',
`This will permanently remove all data. This action cannot be undone.`
)
- }, [type, t, message])
+ }, [type, t, message, identifier, deletionItemName])
return (
-
+
- {displayMessageContent}
+
+ {displayMessageContent}
+
+
{withForm && (
-
+
+ )}
+
+ {violation && (
+
+ {bypassable
+ ? t(
+ 'component:deleteDialog.violationMessages.bypassed',
+ `Some rules will be bypassed while deleting ${type}`,
+ { type: type }
+ )
+ : t('component:deleteDialog.violationMessages.notAllow', `Some rules don't allow you to delete ${type}`, {
+ type: type
+ })}
+
)}
{!!error && (
-
+
Failed to perform delete operation
{getErrorMessage(error as Error, 'Failed to perform delete operation')}
@@ -88,7 +154,9 @@ export const DeleteAlertDialog: FC = ({
)}
- Yes, delete {type}
+
+ {violation && bypassable ? `Bypass rules and delete` : `Yes`}
+
)
diff --git a/packages/ui/src/components/drawer/Drawer.Tagline.tsx b/packages/ui/src/components/drawer/Drawer.Tagline.tsx
index 64b7cf372e..3668b58881 100644
--- a/packages/ui/src/components/drawer/Drawer.Tagline.tsx
+++ b/packages/ui/src/components/drawer/Drawer.Tagline.tsx
@@ -1,8 +1,8 @@
-import { HTMLAttributes } from 'react'
+import { forwardRef, HTMLAttributes } from 'react'
import { cn } from '@/utils'
-export const DrawerTagline = ({ className, ...props }: HTMLAttributes) => (
-
+export const DrawerTagline = forwardRef>(
+ ({ className, ...props }, ref) =>
)
DrawerTagline.displayName = 'DrawerTagline'
diff --git a/packages/ui/src/components/drawer/DrawerBody.tsx b/packages/ui/src/components/drawer/DrawerBody.tsx
index 35260e906f..27d2c78252 100644
--- a/packages/ui/src/components/drawer/DrawerBody.tsx
+++ b/packages/ui/src/components/drawer/DrawerBody.tsx
@@ -1,28 +1,33 @@
+import { forwardRef } from 'react'
+
import { ScrollArea, ScrollAreaProps, useScrollArea } from '@/components'
import { cn } from '@/utils'
-export const DrawerBody = (props: ScrollAreaProps & { scrollAreaClassName?: string }) => {
- const { isTop, isBottom, onScrollTop, onScrollBottom } = useScrollArea(props)
- const { className, children, scrollAreaClassName, classNameContent, ...restProps } = props
+export const DrawerBody = forwardRef(
+ (props, ref) => {
+ const { isTop, isBottom, onScrollTop, onScrollBottom } = useScrollArea(props)
+ const { className, children, scrollAreaClassName, classNameContent, ...restProps } = props
- return (
-
-
- {children}
-
-
- )
-}
+
+ {children}
+
+
+ )
+ }
+)
DrawerBody.displayName = 'DrawerBody'
diff --git a/packages/ui/src/components/drawer/DrawerContent.tsx b/packages/ui/src/components/drawer/DrawerContent.tsx
index ad7675e327..472f83e2c0 100644
--- a/packages/ui/src/components/drawer/DrawerContent.tsx
+++ b/packages/ui/src/components/drawer/DrawerContent.tsx
@@ -46,29 +46,45 @@ export const DrawerContent = forwardRef {
const { portalContainer } = usePortal()
- const { direction } = useDrawerContext()
+ const { direction, modal } = useDrawerContext()
+
+ const withCustomOverlay = forceWithOverlay && modal === false
+
+ const Content = (
+
+ {!hideClose && (
+
+
+
+
+
+ )}
+ {children}
+
+ )
return (
-
-
-
- {!hideClose && (
-
-
-
-
-
- )}
- {children}
-
+ {/* !!! */}
+ {/* For the scroll to work when using the Drawer with modal === true in Shadow DOM, the Overlay needs to wrap the Content */}
+ {/* Here’s the issue for the scroll bug in Shadow DOM, works same for Vaul - https://github.com/radix-ui/primitives/issues/3353 */}
+ {modal ? (
+
+ {Content}
+
+ ) : (
+ <>
+
+ {Content}
+ >
+ )}
)
}
diff --git a/packages/ui/src/components/drawer/DrawerFooter.tsx b/packages/ui/src/components/drawer/DrawerFooter.tsx
index f06e8a7ad7..e210149c81 100644
--- a/packages/ui/src/components/drawer/DrawerFooter.tsx
+++ b/packages/ui/src/components/drawer/DrawerFooter.tsx
@@ -1,8 +1,8 @@
-import { HTMLAttributes } from 'react'
+import { forwardRef, HTMLAttributes } from 'react'
import { cn } from '@/utils'
-export const DrawerFooter = ({ className, ...props }: HTMLAttributes) => (
-
+export const DrawerFooter = forwardRef>(
+ ({ className, ...props }, ref) =>
)
DrawerFooter.displayName = 'DrawerFooter'
diff --git a/packages/ui/src/components/drawer/DrawerHeader.tsx b/packages/ui/src/components/drawer/DrawerHeader.tsx
index ab2b73c4fe..1fbe59ace2 100644
--- a/packages/ui/src/components/drawer/DrawerHeader.tsx
+++ b/packages/ui/src/components/drawer/DrawerHeader.tsx
@@ -1,4 +1,4 @@
-import { Children, HTMLAttributes, ReactNode } from 'react'
+import { Children, forwardRef, HTMLAttributes, ReactNode } from 'react'
import { IconV2, IconV2NamesType, LogoV2, LogoV2NamesType } from '@/components'
import { cn, getComponentDisplayName } from '@/utils'
@@ -28,39 +28,41 @@ type DrawerHeaderNoIconOrLogoProps = {
export type DrawerHeaderProps = DrawerHeaderBaseProps &
(DrawerHeaderIconOnlyProps | DrawerHeaderLogoOnlyProps | DrawerHeaderNoIconOrLogoProps)
-export const DrawerHeader = ({ className, children, icon, logo, ...props }: DrawerHeaderProps) => {
- const IconOrLogoComp =
- (!!icon && ) ||
- (!!logo && ) ||
- null
+export const DrawerHeader = forwardRef(
+ ({ className, children, icon, logo, ...props }, ref) => {
+ const IconOrLogoComp =
+ (!!icon && ) ||
+ (!!logo && ) ||
+ null
- const { titleChildren, otherChildren } = Children.toArray(children).reduce<{
- titleChildren: ReactNode[]
- otherChildren: ReactNode[]
- }>(
- (acc, child) => {
- const displayName = getComponentDisplayName(child)
+ const { titleChildren, otherChildren } = Children.toArray(children).reduce<{
+ titleChildren: ReactNode[]
+ otherChildren: ReactNode[]
+ }>(
+ (acc, child) => {
+ const displayName = getComponentDisplayName(child)
- if (displayName === DrawerPrimitive.Title.displayName || displayName === DrawerTagline.displayName) {
- acc.titleChildren.push(child)
- } else {
- acc.otherChildren.push(child)
- }
- return acc
- },
- { titleChildren: [], otherChildren: [] }
- )
+ if (displayName === DrawerPrimitive.Title.displayName || displayName === DrawerTagline.displayName) {
+ acc.titleChildren.push(child)
+ } else {
+ acc.otherChildren.push(child)
+ }
+ return acc
+ },
+ { titleChildren: [], otherChildren: [] }
+ )
- return (
-
- {(!!titleChildren.length || !!IconOrLogoComp) && (
-
- {IconOrLogoComp}
- {titleChildren}
-
- )}
- {otherChildren}
-
- )
-}
+ return (
+
+ {(!!titleChildren.length || !!IconOrLogoComp) && (
+
+ {IconOrLogoComp}
+ {titleChildren}
+
+ )}
+ {otherChildren}
+
+ )
+ }
+)
DrawerHeader.displayName = 'DrawerHeader'
diff --git a/packages/ui/src/components/drawer/DrawerOverlay.tsx b/packages/ui/src/components/drawer/DrawerOverlay.tsx
index 869737ba06..d7fe396aa8 100644
--- a/packages/ui/src/components/drawer/DrawerOverlay.tsx
+++ b/packages/ui/src/components/drawer/DrawerOverlay.tsx
@@ -7,11 +7,9 @@ import { useDrawerContext } from './drawer-context'
export const DrawerOverlay = forwardRef<
ElementRef,
- ComponentPropsWithoutRef & { forceWithOverlay?: boolean }
->(({ className, forceWithOverlay, ...props }, ref) => {
- const { nested, isParentOpen, modal } = useDrawerContext()
-
- const withCustomOverlay = forceWithOverlay && modal === false
+ ComponentPropsWithoutRef & { withCustomOverlay?: boolean }
+>(({ className, withCustomOverlay = false, ...props }, ref) => {
+ const { nested, isParentOpen } = useDrawerContext()
if (withCustomOverlay) {
return (
diff --git a/packages/ui/src/components/drawer/DrawerRoot.tsx b/packages/ui/src/components/drawer/DrawerRoot.tsx
index 81362c8bf8..449f36f2fb 100644
--- a/packages/ui/src/components/drawer/DrawerRoot.tsx
+++ b/packages/ui/src/components/drawer/DrawerRoot.tsx
@@ -69,7 +69,7 @@ export const DrawerRoot = ({
return (
{nested && FakeTriggers}
diff --git a/packages/ui/src/components/dropdown-menu.tsx b/packages/ui/src/components/dropdown-menu.tsx
index b7b8de04a3..a5e31f281b 100644
--- a/packages/ui/src/components/dropdown-menu.tsx
+++ b/packages/ui/src/components/dropdown-menu.tsx
@@ -1,7 +1,15 @@
-import { ComponentPropsWithoutRef, ElementRef, forwardRef, HTMLAttributes, ReactNode } from 'react'
+import {
+ ComponentPropsWithoutRef,
+ ElementRef,
+ forwardRef,
+ HTMLAttributes,
+ KeyboardEvent,
+ ReactNode,
+ useRef
+} from 'react'
import { usePortal, useTranslation } from '@/context'
-import { cn, filterChildrenByDisplayNames } from '@/utils'
+import { cn, filterChildrenByDisplayNames, useMergeRefs } from '@/utils'
import { Avatar, AvatarProps } from '@components/avatar'
import { Layout } from '@components/layout'
import { ScrollArea, ScrollAreaProps } from '@components/scroll-area'
@@ -64,24 +72,88 @@ const innerComponentsDisplayNames = [
interface DropdownMenuContentProps extends ComponentPropsWithoutRef {
isSubContent?: boolean
scrollAreaProps?: Omit
+ onOpenAutoFocus?: (event: Event) => void
}
const DropdownMenuContent = forwardRef, DropdownMenuContentProps>(
- ({ className, children: _children, sideOffset = 4, isSubContent, scrollAreaProps, ...props }, ref) => {
+ (
+ {
+ className,
+ children: _children,
+ sideOffset = 4,
+ isSubContent,
+ scrollAreaProps,
+ onKeyDownCapture: propOnKeyDownCapture,
+ ...props
+ },
+ ref
+ ) => {
const { portalContainer } = usePortal()
+ const contentRef = useRef(null)
const Primitive = isSubContent ? DropdownMenuPrimitive.SubContent : DropdownMenuPrimitive.Content
const header = filterChildrenByDisplayNames(_children, [displayNames.header])[0]
const footer = filterChildrenByDisplayNames(_children, [displayNames.footer])[0]
const children = filterChildrenByDisplayNames(_children, [displayNames.header, displayNames.footer], true)
+ const mergedRef = useMergeRefs([
+ node => {
+ if (!node) return
+
+ contentRef.current = node
+ },
+ ref
+ ])
+
+ /**
+ * This code is executed only when inside a ShadowRoot
+ */
+ const onKeyDownCaptureHandler = (e: KeyboardEvent) => {
+ /**
+ * To block further code execution in onKeyDownCaptureHandler,
+ * need to call e.preventDefault() inside propOnKeyDownCapture.
+ */
+ propOnKeyDownCapture?.(e)
+ if (e.defaultPrevented || e.isDefaultPrevented?.()) return
+
+ if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return
+
+ const rootEl = contentRef.current
+ if (!rootEl) return
+
+ const rootNode = rootEl.getRootNode()
+
+ if (!(rootNode instanceof ShadowRoot)) return
+
+ const items = Array.from(
+ rootEl.querySelectorAll('[data-radix-collection-item]:not([data-disabled])[role*="menuitem"]')
+ )
+
+ if (!items.length) return
+
+ const activeEl = rootNode instanceof ShadowRoot ? rootNode.activeElement : document.activeElement
+ let idx = items.findIndex(el => el === activeEl || (activeEl instanceof Element && el.contains(activeEl)))
+
+ if (idx === -1) {
+ idx = e.key === 'ArrowDown' ? -1 : 0
+ }
+
+ const next =
+ e.key === 'ArrowDown' ? (idx + 1 + items.length) % items.length : (idx - 1 + items.length) % items.length
+
+ e.preventDefault()
+ e.stopPropagation()
+ items[next]?.focus()
+ }
+
return (
event.preventDefault()}
+ onKeyDownCapture={onKeyDownCaptureHandler}
{...props}
>
{!!header && {header}}
@@ -155,10 +227,28 @@ interface DropdownMenuItemProps
extends Omit,
Omit, 'title' | 'prefix'> {
prefix?: ReactNode
+ subContentProps?: Omit
+ subMenuProps?: ComponentPropsWithoutRef
}
const DropdownMenuItem = forwardRef, DropdownMenuItemProps>(
- ({ className, children, title, description, label, shortcut, checkmark, prefix, tag, ...props }, ref) => {
+ (
+ {
+ className,
+ children,
+ title,
+ description,
+ label,
+ shortcut,
+ checkmark,
+ prefix,
+ tag,
+ subContentProps,
+ subMenuProps,
+ ...props
+ },
+ ref
+ ) => {
const filteredChildren = filterChildrenByDisplayNames(children, innerComponentsDisplayNames)
const withChildren = filteredChildren.length > 0
@@ -170,7 +260,7 @@ const DropdownMenuItem = forwardRef
+
- {filteredChildren}
+
+ {filteredChildren}
+
)
}
@@ -194,7 +286,10 @@ DropdownMenuItem.displayName = displayNames.item
interface DropdownMenuCheckboxItemProps
extends Omit,
- Omit, 'title' | 'onSelect'> {}
+ Omit, 'title' | 'onSelect'> {
+ subContentProps?: Omit
+ subMenuProps?: ComponentPropsWithoutRef
+}
const DropdownMenuCheckboxItem = forwardRef<
ElementRef,
@@ -213,6 +308,8 @@ const DropdownMenuCheckboxItem = forwardRef<
tag,
onCheckedChange,
onClick,
+ subContentProps,
+ subMenuProps,
...props
},
ref
@@ -227,9 +324,9 @@ const DropdownMenuCheckboxItem = forwardRef<
{checked && (
{checked === 'indeterminate' ? (
-
+
) : (
-
+
)}
)}
@@ -239,7 +336,7 @@ const DropdownMenuCheckboxItem = forwardRef<
if (withChildren) {
return (
-
+
- {filteredChildren}
+
+ {filteredChildren}
+
)
}
@@ -402,20 +501,22 @@ const DropdownMenuSeparator = forwardRef<
))
DropdownMenuSeparator.displayName = displayNames.separator
-const DropdownMenuHeader = ({ className, ...props }: HTMLAttributes) => (
-
+const DropdownMenuHeader = forwardRef>(
+ ({ className, ...props }, ref) =>
)
DropdownMenuHeader.displayName = displayNames.header
-const DropdownMenuFooter = ({ className, ...props }: HTMLAttributes) => (
-
+const DropdownMenuFooter = forwardRef>(
+ ({ className, ...props }, ref) =>
)
DropdownMenuFooter.displayName = displayNames.footer
-const DropdownMenuSpinner = ({ className, ...props }: HTMLAttributes) => (
-
-
-
+const DropdownMenuSpinner = forwardRef>(
+ ({ className, ...props }, ref) => (
+
+
+
+ )
)
DropdownMenuSpinner.displayName = displayNames.spinner
@@ -430,7 +531,11 @@ const DropdownMenuNoOptions = ({ className, children, ...props }: Omit) =>
+const DropdownMenuSlot = forwardRef>((props, ref) => (
+
+ {props.children}
+
+))
DropdownMenuSlot.displayName = displayNames.slot
const DropdownMenu = {
diff --git a/packages/ui/src/components/favorite.tsx b/packages/ui/src/components/favorite.tsx
index 14ac03d49d..800d046195 100644
--- a/packages/ui/src/components/favorite.tsx
+++ b/packages/ui/src/components/favorite.tsx
@@ -13,12 +13,13 @@ const Favorite: FC = ({ isFavorite = false, onFavoriteToggle
size="sm"
variant="transparent"
selectedVariant="primary"
+ selected={isFavorite}
prefixIcon={isFavorite ? 'star-solid' : 'star'}
prefixIconProps={{
className: isFavorite ? 'text-cn-icon-yellow' : 'text-cn-foreground-2',
size: '2xs'
}}
- onChange={(selected: boolean) => onFavoriteToggle(!selected)}
+ onChange={(selected: boolean) => onFavoriteToggle(selected)}
/>
)
diff --git a/packages/ui/src/components/file-additions-trigger.tsx b/packages/ui/src/components/file-additions-trigger.tsx
index 2d351430a1..425b320c05 100644
--- a/packages/ui/src/components/file-additions-trigger.tsx
+++ b/packages/ui/src/components/file-additions-trigger.tsx
@@ -16,7 +16,8 @@ export const FileAdditionsTrigger: FC = ({ pathNewFil
- {t('views:repos.create-file', 'Create file')}
+
+ {t('views:repos.createFile', 'Create File')}
@@ -26,14 +27,14 @@ export const FileAdditionsTrigger: FC = ({ pathNewFil
- {t('views:repos.create-file', 'Create file')}
+ {t('views:repos.createFile', 'Create File')}
}
/>
- {t('views:repos.upload-files', 'Upload files')}
+ {t('views:repos.uploadFiles', 'Upload Files')}
}
/>
diff --git a/packages/ui/src/components/file-control-bars/file-editor-control-bar.tsx b/packages/ui/src/components/file-control-bars/file-editor-control-bar.tsx
index 7056dba8c5..ebe27b8a2d 100644
--- a/packages/ui/src/components/file-control-bars/file-editor-control-bar.tsx
+++ b/packages/ui/src/components/file-control-bars/file-editor-control-bar.tsx
@@ -1,12 +1,18 @@
+import { FC } from 'react'
+
import { StackedList, Tabs } from '@/components'
-export const FileEditorControlBar = () => {
+export interface FileEditorControlBarProps {
+ showPreview?: boolean
+}
+
+export const FileEditorControlBar: FC = ({ showPreview = true }) => {
return (
Edit
- Preview
+ {showPreview && Preview }
diff --git a/packages/ui/src/components/file-control-bars/file-viewer-control-bar.tsx b/packages/ui/src/components/file-control-bars/file-viewer-control-bar.tsx
index 56c596d52a..14455f6893 100644
--- a/packages/ui/src/components/file-control-bars/file-viewer-control-bar.tsx
+++ b/packages/ui/src/components/file-control-bars/file-viewer-control-bar.tsx
@@ -8,6 +8,7 @@ import {
Separator,
StackedList,
Tabs,
+ Tag,
Text,
ViewTypeValue
} from '@/components'
@@ -23,6 +24,7 @@ export interface FileViewerControlBarProps {
handleEditFile: () => void
handleOpenDeleteDialog: () => void
refType?: BranchSelectorTab
+ isGitLfsObject?: boolean
}
export const FileViewerControlBar: FC = ({
@@ -31,6 +33,7 @@ export const FileViewerControlBar: FC = ({
fileBytesSize,
fileContent,
url,
+ isGitLfsObject,
handleDownloadFile,
handleEditFile,
handleOpenDeleteDialog,
@@ -43,7 +46,8 @@ export const FileViewerControlBar: FC = ({
const RightDetails = () => {
return (
-
+ {isGitLfsObject && }
+
{`${fileContent?.split('\n').length || 0} lines`}
{fileBytesSize}
@@ -61,10 +65,7 @@ export const FileViewerControlBar: FC = ({
content: (
<>
- Delete}
- />
+ Delete} />
>
),
contentProps: {
@@ -80,7 +81,7 @@ export const FileViewerControlBar: FC = ({
return (
-
+
{isMarkdown && Preview }
Code
diff --git a/packages/ui/src/components/file-explorer.tsx b/packages/ui/src/components/file-explorer.tsx
index 7dcbeb5a0c..887ba9e11f 100644
--- a/packages/ui/src/components/file-explorer.tsx
+++ b/packages/ui/src/components/file-explorer.tsx
@@ -1,68 +1,104 @@
-import { ReactNode } from 'react'
+import { ForwardedRef, forwardRef, ReactNode } from 'react'
-import { Accordion, GridProps, IconPropsV2, IconV2, Layout, Text } from '@/components'
-import { useRouterContext } from '@/context'
+import { Accordion, GridProps, IconPropsV2, IconV2, Layout, Text, Tooltip, TooltipProps } from '@/components'
+import { LinkProps, useRouterContext } from '@/context'
import { cn } from '@utils/cn'
-interface ItemProps extends GridProps {
+interface BaseItemProps {
icon: NonNullable
isActive?: boolean
}
-const Item = ({ className, children, icon, isActive, ...props }: ItemProps) => {
- return (
-
-
-
- {children}
-
-
- )
+interface DefaultItemProps extends BaseItemProps, GridProps {
+ link?: never
+}
+
+interface LinkItemProps extends BaseItemProps, Omit {
+ link?: LinkProps['to']
}
+type ItemProps = DefaultItemProps | LinkItemProps
+
+const Item = forwardRef(
+ ({ className, children, icon, isActive, link, ...props }: ItemProps, ref) => {
+ const { Link } = useRouterContext()
+
+ const commonClassnames = cn(
+ 'w-[fill-available] py-cn-2xs pr-1.5 rounded text-cn-foreground-2 hover:text-cn-foreground-1 hover:bg-cn-background-hover focus-visible:text-cn-foreground-1 focus-visible:bg-cn-background-hover focus-visible:outline-none',
+ {
+ 'bg-cn-background-selected text-cn-foreground-1': isActive,
+ 'grid items-center justify-start gap-cn-2xs grid-flow-col': !!link
+ },
+ className
+ )
+
+ return link ? (
+ }
+ to={link}
+ className={commonClassnames}
+ {...(props as Omit)}
+ >
+
+
+ {children}
+
+
+ ) : (
+
+
+
+ {children}
+
+
+ )
+ }
+)
+Item.displayName = 'FileExplorerItem'
+
interface FolderItemProps {
children: ReactNode
level: number
value?: string
isActive?: boolean
content?: ReactNode
- link: string
+ link?: string
}
function FolderItem({ children, value = '', isActive, content, link, level }: FolderItemProps) {
- const { Link } = useRouterContext()
+ const itemElement = (
+ -
+ {children}
+
+ )
return (
-
+
-
- -
- {children}
-
-
+ {itemElement}
{!!content && (
{content}
@@ -77,22 +113,25 @@ interface FileItemProps {
children: ReactNode
isActive?: boolean
link?: string
+ onClick?: () => void
+ tooltip?: TooltipProps['content']
}
-function FileItem({ children, isActive, level, link }: FileItemProps) {
- const { Link } = useRouterContext()
+function FileItem({ children, isActive, level, link, onClick, tooltip }: FileItemProps) {
const comp = (
-
{children}
)
- return link ? {comp} : comp
+ return tooltip ? {comp} : comp
}
interface RootProps {
diff --git a/packages/ui/src/components/filters/filter-box-wrapper.tsx b/packages/ui/src/components/filters/filter-box-wrapper.tsx
index 06b685c30b..c3a93172bc 100644
--- a/packages/ui/src/components/filters/filter-box-wrapper.tsx
+++ b/packages/ui/src/components/filters/filter-box-wrapper.tsx
@@ -67,23 +67,15 @@ const FilterBoxWrapper = ({
{filterLabel}
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/packages/ui/src/components/filters/filters-bar/actions/variants/combo-box.tsx b/packages/ui/src/components/filters/filters-bar/actions/variants/combo-box.tsx
index 786375e63d..b4d50331f5 100644
--- a/packages/ui/src/components/filters/filters-bar/actions/variants/combo-box.tsx
+++ b/packages/ui/src/components/filters/filters-bar/actions/variants/combo-box.tsx
@@ -3,7 +3,7 @@ import { ReactNode } from 'react'
import { DropdownMenu, ScrollArea, SearchInput } from '@components/index'
export interface ComboBoxOptions {
- label: string
+ label: string | ReactNode
value: string
}
diff --git a/packages/ui/src/components/filters/filters-field.tsx b/packages/ui/src/components/filters/filters-field.tsx
index ae519c2802..f8b4b0bd9e 100644
--- a/packages/ui/src/components/filters/filters-field.tsx
+++ b/packages/ui/src/components/filters/filters-field.tsx
@@ -3,6 +3,7 @@ import { useMemo, useState } from 'react'
import { Button } from '@components/button'
import { Checkbox } from '@components/checkbox'
import { Label } from '@components/form-primitives'
+import { cn } from '@utils/cn'
import FilterBoxWrapper from './filter-box-wrapper'
import Calendar from './filters-bar/actions/variants/calendar-field'
@@ -26,6 +27,7 @@ export interface FiltersFieldProps<
filterOption: FilterOptionConfig
removeFilter: () => void
valueLabel?: string
+ dropdownContentClassName?: string
shouldOpenFilter: boolean
onOpenChange?: (open: boolean) => void
onChange: (selectedValues: V) => void
@@ -122,6 +124,7 @@ const FiltersField = removeFilter()}
isOpen={isOpen}
setIsOpen={setIsOpen}
diff --git a/packages/ui/src/components/filters/types.ts b/packages/ui/src/components/filters/types.ts
index b92d8574b1..8d8ffd5bcb 100644
--- a/packages/ui/src/components/filters/types.ts
+++ b/packages/ui/src/components/filters/types.ts
@@ -28,12 +28,14 @@ interface FilterOptionConfigBase {
// filter-key with which the filter is identified
value: Key
parser?: Parser
+ defaultValue?: V
+ sticky?: boolean
}
interface ComboBoxFilterOptionConfig extends FilterOptionConfigBase {
type: FilterFieldTypes.ComboBox
filterFieldConfig: {
- options: Array<{ label: string; value: string }>
+ options: Array<{ label: string | React.ReactNode; value: string }>
onSearch?: (query: string) => void
noResultsMessage?: string
loadingMessage?: string
@@ -84,6 +86,7 @@ type FilterOptionConfig>
| CheckboxFilterOptionConfig
| MultiSelectFilterOptionConfig
| CustomFilterOptionConfig
+
type FilterValueTypes = string | number | unknown
export type {
diff --git a/packages/ui/src/components/form-primitives/form-caption.tsx b/packages/ui/src/components/form-primitives/form-caption.tsx
index 4b66ab39b5..0ddd844131 100644
--- a/packages/ui/src/components/form-primitives/form-caption.tsx
+++ b/packages/ui/src/components/form-primitives/form-caption.tsx
@@ -1,56 +1,54 @@
-import { PropsWithChildren } from 'react'
+import { forwardRef, PropsWithChildren } from 'react'
+import { cn } from '@/utils'
import { IconV2 } from '@components/icon-v2'
-import { cn } from '@utils/cn'
-import { cva, VariantProps } from 'class-variance-authority'
-
-const formCaptionVariants = cva('cn-caption', {
- variants: {
- theme: {
- default: '',
- success: 'cn-caption-success',
- danger: 'cn-caption-danger',
- warning: 'cn-caption-warning'
- }
- },
- defaultVariants: {
- theme: 'default'
- }
-})
+import { Text, textVariants } from '@components/text'
+import { VariantProps } from 'class-variance-authority'
+
+type ThemeVariants = 'default' | 'success' | 'danger' | 'warning'
+type TextColorVariants = VariantProps['color']
type FormCaptionProps = {
- theme?: VariantProps['theme']
+ theme?: ThemeVariants
className?: string
disabled?: boolean
}
-export const FormCaption = ({
- theme = 'default',
- className,
- disabled,
- children
-}: PropsWithChildren) => {
- /**
- * Return null if no message, errorMessage, or warningMessage is provided
- */
- if (!children) {
- return null
- }
-
- const canShowIcon = theme === 'danger' || theme === 'warning'
+export const FormCaption = forwardRef>(
+ ({ theme = 'default', className, disabled, children }, ref) => {
+ /**
+ * Return null if no message, errorMessage, or warningMessage is provided
+ */
+ if (!children) {
+ return null
+ }
- /**
- * cross-circle - danger
- * triangle-warning - warning
- */
- const effectiveIconName = theme === 'danger' ? 'xmark-circle' : 'warning-triangle'
+ const canShowIcon = theme === 'danger' || theme === 'warning'
+
+ /**
+ * cross-circle - danger
+ * triangle-warning - warning
+ */
+ const effectiveIconName = theme === 'danger' ? 'xmark-circle' : 'warning-triangle'
+
+ const getColor = (theme: ThemeVariants, disabled?: boolean): TextColorVariants => {
+ if (disabled) return 'disabled'
+ const colorMap: Record = {
+ default: 'foreground-3',
+ success: 'success',
+ danger: 'danger',
+ warning: 'warning'
+ }
+ return colorMap[theme]
+ }
- return (
-
- {canShowIcon && }
- {children}
-
- )
-}
+ return (
+
+ {canShowIcon && }
+ {children}
+
+ )
+ }
+)
FormCaption.displayName = 'FormCaption'
diff --git a/packages/ui/src/components/form-primitives/form-wrapper.tsx b/packages/ui/src/components/form-primitives/form-wrapper.tsx
index 754712b9f6..441fbadc4e 100644
--- a/packages/ui/src/components/form-primitives/form-wrapper.tsx
+++ b/packages/ui/src/components/form-primitives/form-wrapper.tsx
@@ -37,12 +37,13 @@ export function FormWrapper({
control,
onSubmit,
orientation = 'vertical',
+ id,
...props
}: FormWrapperProps) {
return (
-
diff --git a/packages/ui/src/components/form-primitives/textarea.tsx b/packages/ui/src/components/form-primitives/textarea.tsx
index b59623cfeb..28bae12713 100644
--- a/packages/ui/src/components/form-primitives/textarea.tsx
+++ b/packages/ui/src/components/form-primitives/textarea.tsx
@@ -1,4 +1,13 @@
-import { ChangeEvent, forwardRef, TextareaHTMLAttributes, useCallback, useMemo, useState } from 'react'
+import {
+ ChangeEvent,
+ forwardRef,
+ TextareaHTMLAttributes,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState
+} from 'react'
import { CommonInputsProp, ControlGroup, FormCaption, Label } from '@/components'
import { cn, generateAlphaNumericHash, isAnyTypeOf, useMergeRefs } from '@/utils'
@@ -54,10 +63,12 @@ const Textarea = forwardRef(
informerProps,
informerContent,
wrapperClassName,
+ autoFocus,
...props
},
ref
) => {
+ const textareaRef = useRef(null)
const [counter, setCounter] = useState(0)
const isHorizontal = orientation === 'horizontal'
@@ -84,6 +95,8 @@ const Textarea = forwardRef(
node => {
if (!node) return
+ textareaRef.current = node
+
if (isAnyTypeOf(node.value, ['number', 'string'])) {
setCharactersCount(String(node.value))
}
@@ -91,6 +104,16 @@ const Textarea = forwardRef(
ref
])
+ useEffect(() => {
+ if (autoFocus && textareaRef.current) {
+ const t = setTimeout(() => {
+ textareaRef.current?.focus()
+ }, 0)
+
+ return () => clearTimeout(t)
+ }
+ }, [autoFocus])
+
return (
{(!!label || maxCharacters || (isHorizontal && !!caption)) && (
diff --git a/packages/ui/src/components/git-commit-dialog/git-commit-dialog.tsx b/packages/ui/src/components/git-commit-dialog/git-commit-dialog.tsx
index 65cc042471..21e025064c 100644
--- a/packages/ui/src/components/git-commit-dialog/git-commit-dialog.tsx
+++ b/packages/ui/src/components/git-commit-dialog/git-commit-dialog.tsx
@@ -132,7 +132,7 @@ export const GitCommitDialog: FC = ({
-
+
{isFileNameRequired && (
= ({
value={CommitToGitRefOption.DIRECTLY}
label={
<>
- {t('component:commitDialog.form.radioGroup.directly.labelFirst', 'Commit directly to the')}
+ {t('component:commitDialog.form.radioGroup.directly.labelFirst', 'Commit directly to')}
- {t('component:commitDialog.form.radioGroup.directly.labelSecond', 'branch')}
>
}
/>
@@ -194,8 +191,7 @@ export const GitCommitDialog: FC = ({
'Create a new branch for this commit and start a pull request'
)}
caption={
- // TODO: Add correct path
-
+
{t('component:commitDialog.form.radioGroup.new.caption', 'Learn more about pull requests')}
}
@@ -252,13 +248,13 @@ export const GitCommitDialog: FC = ({
{t('component:cancel', 'Cancel')}
{!bypassable ? (
-
+
{isSubmitting
? t('component:commitDialog.form.submit.loading', 'Committing...')
: t('component:commitDialog.form.submit.default', 'Commit changes')}
) : (
-
+
{commitToGitRefValue === CommitToGitRefOption.NEW_BRANCH
? t('component:commitDialog.form.submit.bypassable.new', 'Bypass rules and commit via new branch')
: t('component:commitDialog.form.submit.bypassable.directly', 'Bypass rules and commit directly')}
diff --git a/packages/ui/src/components/icon-v2/icon-name-map.ts b/packages/ui/src/components/icon-v2/icon-name-map.ts
index de4e667ea7..70f8e622cc 100644
--- a/packages/ui/src/components/icon-v2/icon-name-map.ts
+++ b/packages/ui/src/components/icon-v2/icon-name-map.ts
@@ -113,6 +113,7 @@ import CollapseCode from './icons/collapse-code.svg'
import CollapseSidebar from './icons/collapse-sidebar.svg'
import Collapse from './icons/collapse.svg'
import Community from './icons/community.svg'
+import Connect from './icons/connect.svg'
import ConnectorsSolid from './icons/connectors-solid.svg'
import Connectors from './icons/connectors.svg'
import ControlSlider from './icons/control-slider.svg'
@@ -185,6 +186,7 @@ import FilterSolid from './icons/filter-solid.svg'
import Filter from './icons/filter.svg'
import Finder from './icons/finder.svg'
import FingerprintWindow from './icons/fingerprint-window.svg'
+import FlagSolid from './icons/flag-solid.svg'
import FlashSolid from './icons/flash-solid.svg'
import Flash from './icons/flash.svg'
import FolderMinus from './icons/folder-minus.svg'
@@ -590,6 +592,7 @@ export const IconNameMapV2 = {
'collapse-sidebar': CollapseSidebar,
collapse: Collapse,
community: Community,
+ connect: Connect,
'connectors-solid': ConnectorsSolid,
connectors: Connectors,
'control-slider': ControlSlider,
@@ -662,6 +665,7 @@ export const IconNameMapV2 = {
filter: Filter,
finder: Finder,
'fingerprint-window': FingerprintWindow,
+ 'flag-solid': FlagSolid,
'flash-solid': FlashSolid,
flash: Flash,
'folder-minus': FolderMinus,
diff --git a/packages/ui/src/components/icon-v2/icon-v2.tsx b/packages/ui/src/components/icon-v2/icon-v2.tsx
index fd87967953..b342e03207 100644
--- a/packages/ui/src/components/icon-v2/icon-v2.tsx
+++ b/packages/ui/src/components/icon-v2/icon-v2.tsx
@@ -1,13 +1,15 @@
-import { FC, SVGProps } from 'react'
+import { forwardRef, SVGProps } from 'react'
import { cn } from '@utils/cn'
import { cva, VariantProps } from 'class-variance-authority'
import { IconNameMapV2 } from './icon-name-map'
+export const IconV2DisplayName = 'IconV2'
+
export type IconV2NamesType = keyof typeof IconNameMapV2
-const iconVariants = cva('cn-icon', {
+export const iconVariants = cva('cn-icon', {
variants: {
size: {
'2xs': 'cn-icon-2xs',
@@ -41,25 +43,27 @@ interface IconFallbackPropsV2 extends BaseIconPropsV2 {
export type IconPropsV2 = IconDefaultPropsV2 | IconFallbackPropsV2
-const IconV2: FC = ({ name, size = 'sm', className, skipSize = false, fallback }) => {
- const Component = name ? IconNameMapV2[name] : undefined
- const sizeClasses = skipSize ? '' : iconVariants({ size })
+const IconV2 = forwardRef(
+ ({ name, size = 'sm', className, skipSize = false, fallback }, ref) => {
+ const Component = name ? IconNameMapV2[name] : undefined
+ const sizeClasses = skipSize ? '' : iconVariants({ size })
- if (!Component && fallback) {
- console.warn(`Icon "${name}" not found, falling back to "${fallback}".`)
- const FallbackComponent = IconNameMapV2[fallback]
+ if (!Component && fallback) {
+ console.warn(`Icon "${name}" not found, falling back to "${fallback}".`)
+ const FallbackComponent = IconNameMapV2[fallback]
- return
- }
+ return
+ }
- if (!Component) {
- console.warn(`Icon "${name}" not found in IconNameMapV2.`)
- return null
- }
+ if (!Component) {
+ console.warn(`Icon "${name}" not found in IconNameMapV2.`)
+ return null
+ }
- return
-}
+ return
+ }
+)
export { IconV2 }
-IconV2.displayName = 'IconV2'
+IconV2.displayName = IconV2DisplayName
diff --git a/packages/ui/src/components/icon-v2/icons/connect.svg b/packages/ui/src/components/icon-v2/icons/connect.svg
new file mode 100644
index 0000000000..09af32dd7d
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/connect.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/flag-solid.svg b/packages/ui/src/components/icon-v2/icons/flag-solid.svg
new file mode 100644
index 0000000000..b664cc920c
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/flag-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/folder-minus.svg b/packages/ui/src/components/icon-v2/icons/folder-minus.svg
index 8db71dac8c..c4b9f92ce7 100644
--- a/packages/ui/src/components/icon-v2/icons/folder-minus.svg
+++ b/packages/ui/src/components/icon-v2/icons/folder-minus.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/folder-plus.svg b/packages/ui/src/components/icon-v2/icons/folder-plus.svg
index 10114dde86..82f0ed94c7 100644
--- a/packages/ui/src/components/icon-v2/icons/folder-plus.svg
+++ b/packages/ui/src/components/icon-v2/icons/folder-plus.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/folder-settings.svg b/packages/ui/src/components/icon-v2/icons/folder-settings.svg
index 07a57135df..b0a674d9de 100644
--- a/packages/ui/src/components/icon-v2/icons/folder-settings.svg
+++ b/packages/ui/src/components/icon-v2/icons/folder-settings.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/folder-warning.svg b/packages/ui/src/components/icon-v2/icons/folder-warning.svg
index 9e2c4b4558..f5dab0a75e 100644
--- a/packages/ui/src/components/icon-v2/icons/folder-warning.svg
+++ b/packages/ui/src/components/icon-v2/icons/folder-warning.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/folder.svg b/packages/ui/src/components/icon-v2/icons/folder.svg
index 2f924ddc82..71e387d1db 100644
--- a/packages/ui/src/components/icon-v2/icons/folder.svg
+++ b/packages/ui/src/components/icon-v2/icons/folder.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/sidebar.svg b/packages/ui/src/components/icon-v2/icons/sidebar.svg
index c92a34a473..6476611df5 100644
--- a/packages/ui/src/components/icon-v2/icons/sidebar.svg
+++ b/packages/ui/src/components/icon-v2/icons/sidebar.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/warning-triangle-solid.svg b/packages/ui/src/components/icon-v2/icons/warning-triangle-solid.svg
index 4ea443c97a..9026916abb 100644
--- a/packages/ui/src/components/icon-v2/icons/warning-triangle-solid.svg
+++ b/packages/ui/src/components/icon-v2/icons/warning-triangle-solid.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/illustration/illustration.tsx b/packages/ui/src/components/illustration/illustration.tsx
index 7d5463cd6d..de3d4525eb 100644
--- a/packages/ui/src/components/illustration/illustration.tsx
+++ b/packages/ui/src/components/illustration/illustration.tsx
@@ -1,4 +1,4 @@
-import { FC, SVGProps } from 'react'
+import { forwardRef, SVGProps } from 'react'
import { useTheme } from '@/context'
import { cn } from '@utils/cn'
@@ -14,39 +14,33 @@ export interface IllustrationProps extends SVGProps {
themeDependent?: boolean
}
-const Illustration: FC = ({
- name,
- size = 112,
- height,
- width,
- className,
- themeDependent = false
-}) => {
- const { isLightTheme } = useTheme()
-
- const isLightIconAvailable = !!IllustrationsNameMap[`${name}-light` as keyof typeof IllustrationsNameMap]
-
- const Component =
- themeDependent && isLightTheme && isLightIconAvailable
- ? IllustrationsNameMap[`${name}-light` as keyof typeof IllustrationsNameMap]
- : IllustrationsNameMap[name]
-
- if (!Component) {
- console.warn(`Icon "${name}" not found in IllustrationsNameMap.`)
- return null
- }
+const Illustration = forwardRef(
+ ({ name, size = 112, height, width, className, themeDependent = false }, ref) => {
+ const { isLightTheme } = useTheme()
- const shouldInvert = themeDependent && isLightTheme && !isLightIconAvailable
+ const isLightIconAvailable = !!IllustrationsNameMap[`${name}-light` as keyof typeof IllustrationsNameMap]
- const sizeProps = {
- width: width || size,
- height: height || size,
- style: { minWidth: `${width || size}px`, minHeight: `${height || size}px` }
- }
+ const Component =
+ themeDependent && isLightTheme && isLightIconAvailable
+ ? IllustrationsNameMap[`${name}-light` as keyof typeof IllustrationsNameMap]
+ : IllustrationsNameMap[name]
- return
-}
+ if (!Component) {
+ console.warn(`Icon "${name}" not found in IllustrationsNameMap.`)
+ return null
+ }
-export { Illustration }
+ const shouldInvert = themeDependent && isLightTheme && !isLightIconAvailable
+ const sizeProps = {
+ width: width || size,
+ height: height || size,
+ style: { minWidth: `${width || size}px`, minHeight: `${height || size}px` }
+ }
+
+ return
+ }
+)
Illustration.displayName = 'Illustration'
+
+export { Illustration }
diff --git a/packages/ui/src/components/illustration/images/no-data-tags-light.svg b/packages/ui/src/components/illustration/images/no-data-tags-light.svg
index be0d7f1ff3..31f6731898 100644
--- a/packages/ui/src/components/illustration/images/no-data-tags-light.svg
+++ b/packages/ui/src/components/illustration/images/no-data-tags-light.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/illustration/images/no-data-tags.svg b/packages/ui/src/components/illustration/images/no-data-tags.svg
index 708aff314e..a70f7d6ff2 100644
--- a/packages/ui/src/components/illustration/images/no-data-tags.svg
+++ b/packages/ui/src/components/illustration/images/no-data-tags.svg
@@ -1,72 +1 @@
-
+
\ No newline at end of file
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts
index 3da1ed0618..dbde0abd94 100644
--- a/packages/ui/src/components/index.ts
+++ b/packages/ui/src/components/index.ts
@@ -70,6 +70,9 @@ export * from './resizable'
export * from './time-ago-card'
export * from './informer'
export * from './tag'
+export * from './copy-tag'
+export * from './reset-tag'
+export * from './branch-tag'
export * from './sorts'
export * from './entity-form-layout'
export * from './multi-select'
@@ -97,3 +100,4 @@ export * from './inputs'
export * from './form-input'
export * from './favorite'
export * from './scope'
+export * from './rbac'
diff --git a/packages/ui/src/components/informer.tsx b/packages/ui/src/components/informer.tsx
index b17520b7ff..4ef3399938 100644
--- a/packages/ui/src/components/informer.tsx
+++ b/packages/ui/src/components/informer.tsx
@@ -1,3 +1,5 @@
+import { PropsWithoutRef } from 'react'
+
import { IconPropsV2, IconV2, Tooltip, TooltipProps } from '@/components'
import { cn } from '@/utils'
@@ -13,7 +15,7 @@ export interface InformerProps extends Omit {
export const Informer = ({ className, disabled, iconProps, ...props }: InformerProps) => (
-
+ )} />
)
diff --git a/packages/ui/src/components/input-otp.tsx b/packages/ui/src/components/input-otp.tsx
index 65f6ade090..730c0121b1 100644
--- a/packages/ui/src/components/input-otp.tsx
+++ b/packages/ui/src/components/input-otp.tsx
@@ -48,7 +48,7 @@ const InputOTPSlot = forwardRef(({ index, cla
{char}
{hasFakeCaret && (
-
+
)}
-
-
-
-
-
-
+
+ >
)
}
diff --git a/packages/ui/src/components/markdown-viewer/index.tsx b/packages/ui/src/components/markdown-viewer/index.tsx
index 97c3a1a8c4..f1f108637e 100644
--- a/packages/ui/src/components/markdown-viewer/index.tsx
+++ b/packages/ui/src/components/markdown-viewer/index.tsx
@@ -1,6 +1,6 @@
-import { CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { CSSProperties, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react'
-import { CopyButton, ImageCarousel } from '@/components'
+import { CopyButton, Text } from '@/components'
import MarkdownPreview from '@uiw/react-markdown-preview'
import rehypeExternalLinks from 'rehype-external-links'
import { getCodeString, RehypeRewriteOptions } from 'rehype-rewrite'
@@ -28,9 +28,11 @@ type MarkdownViewerProps = {
className?: string
suggestionBlock?: SuggestionBlock
suggestionCheckSum?: string
- isSuggestion?: boolean
markdownClassName?: string
showLineNumbers?: boolean
+ onCheckboxChange?: (source: string) => void
+ suggestionTitle?: string
+ suggestionFooter?: ReactNode
}
export function MarkdownViewer({
@@ -39,17 +41,23 @@ export function MarkdownViewer({
withBorder = false,
className,
suggestionBlock,
- suggestionCheckSum,
- isSuggestion,
markdownClassName,
- showLineNumbers = false
+ showLineNumbers = false,
+ onCheckboxChange,
+ suggestionTitle,
+ suggestionFooter
}: MarkdownViewerProps) {
const { navigate } = useRouterContext()
- const [isOpen, setIsOpen] = useState(false)
- const [imgEvent, setImageEvent] = useState{children}
+
+
{hasLineNumbers ? (
@@ -236,26 +292,23 @@ export function MarkdownViewer({
code: ({ children = [], className: _className, ...props }) => {
const code = props.node && props.node.children ? getCodeString(props.node.children) : children
- if (
+ const isPossibleSuggestion =
typeof code === 'string' &&
- isSuggestion &&
typeof _className === 'string' &&
'language-suggestion' === _className.split(' ')[0].toLocaleLowerCase()
- ) {
- return
+
+ if (isPossibleSuggestion) {
+ const { isSuggestion } = getIsSuggestion(code)
+
+ if (isSuggestion) {
+ return
+ }
}
- return {children}
+ return {children}
}
}}
/>
-
-
)
diff --git a/packages/ui/src/components/markdown-viewer/style.css b/packages/ui/src/components/markdown-viewer/style.css
index 96c64e2d6f..5c9e8b7458 100644
--- a/packages/ui/src/components/markdown-viewer/style.css
+++ b/packages/ui/src/components/markdown-viewer/style.css
@@ -6,7 +6,7 @@
color-scheme: dark;
--color-fg-default: #dedede;
- --color-fg-muted: #969896;
+ --color-fg-muted: var(--cn-text-3);
--color-fg-subtle: #7aa6da;
--color-canvas-default: var(--cn-bg-1);
@@ -72,7 +72,7 @@
color-scheme: light;
--color-fg-default: #353535;
- --color-fg-muted: #10a567;
+ --color-fg-muted: var(--cn-text-3);
--color-fg-subtle: #386ac3;
--color-canvas-default: var(--cn-bg-1);
@@ -154,6 +154,10 @@
font: var(--cn-comp-markdown-content-paragraph);
@apply text-cn-foreground-2 !max-w-full;
+ &::-webkit-scrollbar-thumb {
+ background-color: var(--cn-comp-scrollbar-thumb);
+ }
+
h1,
h2,
h3,
@@ -202,7 +206,7 @@
}
li {
- @apply marker:text-cn-foreground-1 my-cn-2xs ps-0;
+ @apply marker:text-cn-foreground-2 my-cn-2xs ps-0;
}
ol {
@@ -228,24 +232,30 @@
}
code {
- @apply font-body-code border-cn-borders-2 text-nowrap rounded border bg-[var(--cn-set-brand-surface-bg)] px-1 !text-[var(--cn-set-brand-surface-text)] before:hidden after:hidden;
+ @apply font-body-code px-cn-2xs py-cn-4xs text-nowrap rounded border-0 bg-[var(--cn-set-brand-surface-bg)] !text-[var(--cn-set-blue-soft-text)] before:hidden after:hidden;
+ }
+
+ :not(p) > code {
+ font-size: inherit;
+ line-height: inherit;
}
strong {
font: var(--cn-comp-markdown-content-emphasized);
- @apply text-cn-foreground-1;
+ @apply text-cn-foreground-2;
}
pre {
- @apply font-body-code bg-cn-background-1 border-cn-borders-3 text-cn-foreground-1 mb-0 rounded border py-3 pl-4 pr-3;
+ @apply font-body-code bg-cn-background-1 border-cn-borders-3 text-cn-foreground-1 mb-0 rounded-none border-0 p-0;
code {
- @apply bg-cn-background-1 !text-cn-foreground-1 mr-7 p-0;
+ @apply bg-cn-background-1 !text-cn-foreground-1 rounded-none p-0;
}
}
blockquote {
- @apply border-cn-borders-1 my-cn-md border-l-4 pl-4 pr-0;
+ font: var(--cn-comp-markdown-content-paragraph);
+ @apply text-cn-foreground-3 my-cn-md relative border-0 pl-4 pr-0 before:absolute before:inset-y-0 before:left-0 before:w-1 before:rounded-sm before:bg-[var(--cn-border-2)];
}
table {
@@ -277,17 +287,22 @@
@apply border-0;
}
}
- }
- &.comment {
- @apply !bg-transparent;
+ &.pr-section {
+ @apply font-body-normal !bg-transparent;
- pre code {
- @apply bg-transparent;
- }
+ pre code {
+ @apply bg-transparent;
+ }
- blockquote {
- @apply before:bg-cn-background-softgray relative border-0 before:absolute before:inset-y-0 before:left-0 before:w-[3px] before:rounded-sm;
+ a,
+ blockquote {
+ @apply font-body-normal;
+ }
+
+ strong {
+ @apply font-body-strong;
+ }
}
}
}
diff --git a/packages/ui/src/components/more-actions-tooltip.tsx b/packages/ui/src/components/more-actions-tooltip.tsx
index 3fe30b2227..152671ea90 100644
--- a/packages/ui/src/components/more-actions-tooltip.tsx
+++ b/packages/ui/src/components/more-actions-tooltip.tsx
@@ -6,7 +6,6 @@ import { DropdownMenu } from '@components/dropdown-menu'
import { IconV2, type IconPropsV2 } from '@components/icon-v2'
import { cn } from '@utils/cn'
-import { Layout } from './layout'
import { Text } from './text'
export interface ActionData {
@@ -32,8 +31,8 @@ export interface MoreActionsTooltipProps {
export const MoreActionsTooltip: FC = ({
actions,
iconName = 'more-vert',
- sideOffset = -6,
- alignOffset = 10,
+ sideOffset = 2,
+ alignOffset = 0,
className
}) => {
const { Link } = useRouterContext()
@@ -46,12 +45,17 @@ export const MoreActionsTooltip: FC = ({
className={cn('text-icons-1 hover:text-icons-2 data-[state=open]:text-icons-2')}
variant="ghost"
iconOnly
- size="sm"
+ size="md"
>
-
+
{actions.map((action, idx) =>
action.to ? (
= ({
e.stopPropagation()
}}
>
-
- {action.iconName ? : null}
+ {action.iconName ? (
+
{action.title}
-
- }
- />
+ }
+ />
+ ) : (
+
+ {action.title}
+
+ }
+ />
+ )}
+ ) : action.iconName ? (
+
+ {action.title}
+
+ }
+ iconClassName={cn({ 'text-cn-foreground-danger': action.isDanger })}
+ key={`${action.title}-${idx}`}
+ onClick={e => {
+ e.stopPropagation()
+ action?.onClick?.()
+ }}
+ />
) : (
- {action.iconName ? (
-
- ) : null}
-
- {action.title}
-
-
+
+ {action.title}
+
}
key={`${action.title}-${idx}`}
onClick={e => {
@@ -96,3 +119,4 @@ export const MoreActionsTooltip: FC = ({
)
}
+MoreActionsTooltip.displayName = 'MoreActionsTooltip'
diff --git a/packages/ui/src/components/multi-select.tsx b/packages/ui/src/components/multi-select.tsx
index 06c89c3e1a..37d5023281 100644
--- a/packages/ui/src/components/multi-select.tsx
+++ b/packages/ui/src/components/multi-select.tsx
@@ -20,7 +20,7 @@ import {
IconV2NamesType,
Label,
Layout,
- SkeletonList,
+ Skeleton,
Tag
} from '@/components'
import { generateAlphaNumericHash } from '@/utils'
@@ -335,12 +335,11 @@ export const MultiSelect = forwardRef(
theme={option?.theme}
label={option.key}
value={option?.value || ''}
- showReset={!disabled}
- onReset={() => handleUnselect(option)}
+ actionIcon={disabled ? undefined : 'xmark'}
+ onActionClick={() => handleUnselect(option)}
disabled={disabled}
title={option.title}
icon={option.icon}
- showIcon={Boolean(option.icon)}
/>
)
})}
@@ -364,7 +363,7 @@ export const MultiSelect = forwardRef(
setOpen(true)
inputProps?.onFocus?.(event)
}}
- placeholder={disabled ? '' : placeholder}
+ placeholder={disabled || getSelectedOptions().length > 0 ? '' : placeholder}
className={cn('cn-multi-select-input', inputProps?.className)}
asChild
>
@@ -387,7 +386,7 @@ export const MultiSelect = forwardRef(
}}
>
{isLoading ? (
-
+
) : availableOptions?.length === 0 ? (
disallowCreation ? (
diff --git a/packages/ui/src/components/no-data.tsx b/packages/ui/src/components/no-data.tsx
index 94164f35a9..db16790fc0 100644
--- a/packages/ui/src/components/no-data.tsx
+++ b/packages/ui/src/components/no-data.tsx
@@ -1,31 +1,47 @@
import { Dispatch, FC, ReactNode, SetStateAction } from 'react'
-import { Button, ButtonProps, Illustration, IllustrationsNameType, Layout, Text } from '@/components'
+import {
+ Button,
+ ButtonProps,
+ IconPropsV2,
+ IconV2,
+ Illustration,
+ IllustrationsNameType,
+ Layout,
+ SplitButton,
+ Text
+} from '@/components'
import { useRouterContext } from '@/context'
import { cn } from '@utils/cn'
+import omit from 'lodash-es/omit'
export interface NoDataProps {
title: string
imageName?: IllustrationsNameType
imageSize?: number
description: string[]
- primaryButton?: {
+ primaryButton?: ButtonProps & {
+ icon?: IconPropsV2['name']
label: ReactNode | string
- onClick?: () => void
to?: string
- props?: ButtonProps
}
- secondaryButton?: {
+ secondaryButton?: ButtonProps & {
+ icon?: IconPropsV2['name']
label: ReactNode | string
to?: string
- onClick?: () => void
- props?: ButtonProps
}
withBorder?: boolean
loadState?: string
setLoadState?: Dispatch>
textWrapperClassName?: string
className?: string
+ splitButton?: {
+ label: ReactNode | string
+ options: { value: string; label: string }[]
+ handleOptionChange: (option: string) => void
+ handleButtonClick: () => void
+ props?: ButtonProps
+ }
}
export const NoData: FC = ({
@@ -34,11 +50,11 @@ export const NoData: FC = ({
title,
description,
primaryButton,
-
secondaryButton,
withBorder = false,
textWrapperClassName,
- className
+ className,
+ splitButton
}) => {
const { NavLink } = useRouterContext()
@@ -48,8 +64,8 @@ export const NoData: FC = ({
align="center"
justify="center"
className={cn(
- 'h-full w-full my-auto',
- { 'h-auto grow border border-cn-borders-4 rounded-md': withBorder },
+ 'h-full w-full my-auto py-cn-4xl',
+ { 'h-auto grow border border-cn-borders-3 rounded-md': withBorder },
className
)}
>
@@ -57,43 +73,57 @@ export const NoData: FC = ({
{title}
- {description && (
-
- {description.map((line, index) => (
-
- {line}
-
- ))}
-
- )}
+ {!!description &&
+ description.map((line, index) => (
+
+ {line}
+
+ ))}
- {(primaryButton || secondaryButton) && (
+ {(primaryButton || secondaryButton || splitButton) && (
{primaryButton &&
(primaryButton.to ? (
- primaryButton?.onClick?.()} {...primaryButton.props}>
- {primaryButton.label}
+
+
+ {primaryButton.icon && }
+ {primaryButton.label}
+
) : (
- primaryButton?.onClick?.()} {...primaryButton.props}>
+
+ {primaryButton.icon && }
{primaryButton.label}
))}
{secondaryButton &&
(secondaryButton.to ? (
- secondaryButton?.onClick?.()}
- {...secondaryButton.props}
- >
- {secondaryButton.label}
+
+
+ {secondaryButton.icon && }
+ {secondaryButton.label}
+
) : (
- secondaryButton?.onClick?.()} {...secondaryButton.props}>
+
+ {secondaryButton.icon && }
{secondaryButton.label}
))}
+ {splitButton && (
+
+ dropdownContentClassName="mt-0 min-w-[170px]"
+ handleButtonClick={() => splitButton.handleButtonClick()}
+ handleOptionChange={option => {
+ if (option === 'tag-rule') {
+ splitButton.handleOptionChange(option)
+ }
+ }}
+ options={splitButton.options}
+ >
+ {splitButton.label}
+
+ )}
)}
diff --git a/packages/ui/src/components/node-group.tsx b/packages/ui/src/components/node-group.tsx
index e8fbdf119e..890ca6d374 100644
--- a/packages/ui/src/components/node-group.tsx
+++ b/packages/ui/src/components/node-group.tsx
@@ -33,14 +33,14 @@ function Icon({
- {simpleNodeIcon ? : <>{children}>}
+ {simpleNodeIcon ? : <>{children}>}