@@ -132,7 +144,9 @@ import { DocsPage } from "@/components/docs-page";
{/* Tags with Custom Icon */}
-
Tags with Custom Icon
+
+ Tags with Custom Icon
+
- Tags with Reset
+
+ Tags with Reset
+
- Tags with Icon and Reset
+ Tags with Icon and Reset
` element. Other va
dummyText
- caption-single-line-soft:
- dummyText
+ caption-single-line-light:
+ dummyText
-
-
- {/* Colors
-
- inherit:
- dummyText
-
-
-
- foreground-1:
- dummyText
-
-
- foreground-2:
- dummyText
-
-
- foreground-3:
- dummyText
-
-
- state-disabled:
- dummyText
-
-
- success:
- dummyText
-
-
- warning:
- dummyText
-
-
- danger:
- dummyText
-
*/}
-
`.replaceAll('dummyText', `Consistency in typography helps users navigate content with ease. Always use hierarchy to guide attention and improve readability.`)}
/>
@@ -300,7 +263,7 @@ The `lineClamp` prop controls how many lines of text are shown before truncating
defaultValue: "body-normal",
required: false,
value:
- "'heading-hero' | 'heading-section' | 'heading-subsection' | 'heading-base' | 'heading-small' | 'body-normal' | 'body-single-line-normal' | 'body-strong' | 'body-single-line-strong' | 'body-code' | 'caption-normal' | 'caption-soft' | 'caption-single-line-normal' | 'caption-single-line-soft'",
+ "'heading-hero' | 'heading-section' | 'heading-subsection' | 'heading-base' | 'heading-small' | 'body-normal' | 'body-single-line-normal' | 'body-strong' | 'body-single-line-strong' | 'body-code' | 'caption-normal' | 'caption-soft' | 'caption-single-line-normal' | 'caption-single-line-light'",
},
{
name: "color",
diff --git a/apps/portal/src/content/docs/components/time-ago-card.mdx b/apps/portal/src/content/docs/components/time-ago-card.mdx
index de80cf1b55..b1e7d98ae7 100644
--- a/apps/portal/src/content/docs/components/time-ago-card.mdx
+++ b/apps/portal/src/content/docs/components/time-ago-card.mdx
@@ -90,6 +90,12 @@ return (
required: false,
value: "'TextProps'",
},
+ {
+ name: "tooltipProps",
+ description: "Props for the tooltip element.",
+ required: false,
+ value: "'TooltipProps'",
+ },
{
name: "triggerClassName",
description: "Class names for the trigger element.",
diff --git a/apps/portal/src/content/docs/components/toast.mdx b/apps/portal/src/content/docs/components/toast.mdx
index ff5126e53a..77e3c2cddf 100644
--- a/apps/portal/src/content/docs/components/toast.mdx
+++ b/apps/portal/src/content/docs/components/toast.mdx
@@ -4,332 +4,312 @@ description: Toast component
beta: true
---
-The `Toast` component is used to display notifications to the user. The `Toast.Root` component can be used either controlled or uncontrolled. When used uncontrolled, the `Toast.Root` component automatically handles the open state of the toast. When used controlled, the `Toast.Root` component can be controlled by providing the `open` and `onOpenChange` props.
-
+import { Code, Card, Aside } from "@astrojs/starlight/components";
import { DocsPage } from "../../../components/docs-page";
- {
- const [show, setShow] = React.useState(false)
-
- return (
-
- setShow(curr => !curr)}>{show ? 'Hide' : 'Show'} Toast
+`toast` is a toast component that is used to display notifications to the user. It is handled by `Toaster` component, that is added in the root of the app.
- {show && (
-
- Toast title
- Toast description
-
-
- )}
-
-
- )
+To show a simple toast, use the `toast` function.
-}`}
+
+ toast({
+ title: 'Toast title',
+ description: Array.from({ length: Math.floor(Math.random() * 1000) + 1 })
+ .map(() => 'a')
+ .join(''),
+ options: {
+ duration: 3000,
+ action: {
+ label: 'Undo',
+ onClick: () => {
+ console.log('Undo')
+ }
+ }
+ }
+ })
+ }
+ >
+ Show toast
+ `}
/>
-## Usage
-
-```typescript jsx
-import { Toast } from '@harnessio/ui/components'
-
-//...
-
-return (
- {/* typically placed at the root of the app */}
-
- Toast title
- Toast description
-
-
-
-
-)
-```
+ {
+ console.log('Undo')
+ }
+ }
+ }
+ })
+ `}
+ lang="typescript"
+/>
-## Anatomy
+## Toast types
-All parts of the `Toast` component can be imported and composed as required.
+`toast` can be used to show different types of toasts.
-```typescript jsx
-
-
-
-
-
-
-
-
-
-```
+
+ - Default toast (`toast`)
+ - Success toast (`toast.success`)
+ - Danger toast (`toast.danger`)
+ - Info toast (`toast.info`)
+ - Loading toast (`toast.loading`)
+ - Promise toast (`toast.promise`)
+
-## API Reference
+
-### `Provider`
+
+ Maximum number of toasts that can be displayed at a time is 3.
+ - If duration is not provided, the toast will be closed after 10 seconds by default.
+ - Close button in toast will be hidden only if dismissible is set to false in options. By default, it is set to true.
+ - Action button can be added to toast by providing action in options.
+ - Clicking of action button will execute the callback function provided in action and will not close the toast by default. If you want to close the toast after clicking the action button, you can use toast.dismiss in the callback function.
+ - Promise toast and Loading toast cannot be dismissed by the user, it will be closed after the promise is resolved or rejected (Promise toast) or when the work in progress task is completed (Loading toast).
+ - Promise toast and Loading toast cannot display a description
+
+
-The `Provider` component is used to provide the Toast context to the rest of the app. It is typically placed at the root of the app.
+## Usage
-```typescript jsx
-
- {/* Toast roots and viewport */}
-
-```
+### Success toast
-
+ toast.success({
+ title: 'Toast title',
+ description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magni magnam quibusdam fugit saepe voluptates aperiam perspiciatis ipsam! Ad cupiditate, tempora aspernatur a, veritatis, rem maxime est aliquam natus iure vero. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dignissimos quis reprehenderit quas! Praesentium, delectus perferendis? Dicta, reiciendis? Sint ipsa necessitatibus natus, magni, delectus consequuntur, aperiam asperiores in quasi commodi doloribus?",
+ options: {
+ duration: 5000,
+ action: {
+ label: 'Undo',
+ onClick: () => {
+ console.log('Undo')
+ }
+ }
+ }
+ })
+ }
+ >
+ Show success toast
+ `}
/>
-### `Root`
+### Danger toast
-The `Root` component is used to wrap the content of the toast. The `Root` component can be used either controlled or uncontrolled. When used uncontrolled, the `Root` component automatically handles the open state of the toast. When used controlled, the `Root` component can be controlled by providing the `open` and `onOpenChange` props.
+
+ toast.danger({
+ title: 'Toast title',
+ description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magni magnam quibusdam fugit saepe voluptates aperiam perspiciatis ipsam! Ad cupiditate, tempora aspernatur a, veritatis, rem maxime est aliquam natus iure vero.",
+ options: {
+ duration: 5000,
+ action: {
+ label: 'Undo',
+ onClick: () => {
+ console.log('Undo')
+ }
+ }
+ }
+ })
+ }
+ >
+ Show danger toast
+ `}
+/>
-```typescript jsx
-
- Toast title
- Toast description
-
-
-```
+### Info toast
- void",
- },
- {
- name: "defaultOpen",
- description: "Whether the toast is open by default",
- required: false,
- value: "boolean",
- },
- {
- name: "forceMount",
- description: "Whether the toast should be mounted even if it is not open",
- required: false,
- value: "boolean",
- },
- {
- name: "className",
- description: "Custom class name",
- required: false,
- value: "string",
- },
- ]}
+
+ toast.info({
+ title: 'Toast title',
+ description: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Magni magnam quibusdam fugit saepe voluptates aperiam perspiciatis ipsam! Ad cupiditate, tempora aspernatur a, veritatis, rem maxime est aliquam natus iure vero.",
+ options: {
+ duration: 5000,
+ }
+ })
+ }
+ >
+ Show info toast
+ `}
/>
-### `Title`
+### Loading toast
-The `Title` component is used to display the title of the toast.
+
+ Loading toast needs to be closed manually. use `toast.dismiss` to close the
+ toast using the toast id.
+
-```typescript jsx
-
- Toast title
-
-```
-
- {
+ const _toastId = toast.loading({
+ title: 'Loading data...',
+ options: {
+ action: {
+ label: 'Close',
+ onClick: () => {
+ toast.dismiss(_toastId)
+ }
+ }
+ }
+ })
+ }}
+ >
+ Show loading toast
+
+ `}
/>
-### `Description`
+### Promise toast
-The `Description` component is used to display the description of the toast.
+Promise toast accepts a **promise** as its **first argument** and an **options object** as its **second argument**.
-```typescript jsx
-
- Toast description
-
-```
+
+ Promise toast cannot be dismissed by the user, it will be closed after 7
+ seconds when the promise is resolved or rejected.
+
- (
+ {
+ const _toastId = toast.promise(new Promise((resolve, reject) => {
+ const random = Math.random()
+ setTimeout(() => {
+ if (random > 0.5) {
+ resolve('Promise resolved')
+ } else {
+ reject('Promise rejected')
+ }
+ }, 5000)
+ }), {
+ loadingMessage: 'Promise in progress',
+ successMessage: 'Promise resolved successfully',
+ errorMessage: 'Promise rejected',
+ options: {
+ action: {
+ label: 'Action',
+ onClick: () => {
+ console.log('Action')
+ toast.dismiss(_toastId)
+ },
+ },
+ },
+ })
+ }
+ }
+ >
+ Show promise toast
+
+)
+`}
/>
-### `Close`
-
-The `Close` component is used to close the toast.
+### Toast params
-```typescript jsx
-
- Close
-
-```
+Toast params are the parameters that are passed to the toast function.
void",
+ value: "ReactNode",
},
- ]}
-/>
-
-### `Action`
-
-The `Action` component is used to display a call-to-action button in the toast.
-
-```typescript jsx
-
- Approve
-
-```
-
- void",
+ value: "string",
},
]}
/>
-### `Viewport`
-
-The `Viewport` component is used to display the toast in the viewport.
+### Toast options
-```typescript jsx
-
-```
+Toast options are the configuration object that are passed to the toast parameter.
diff --git a/apps/portal/src/content/docs/components/tree-view.mdx b/apps/portal/src/content/docs/components/tree-view.mdx
new file mode 100644
index 0000000000..6411b0ed75
--- /dev/null
+++ b/apps/portal/src/content/docs/components/tree-view.mdx
@@ -0,0 +1,1132 @@
+---
+title: TreeView
+description: TreeView component for displaying hierarchical data structures
+---
+
+The `TreeView` component is used to display hierarchical data in an expandable/collapsible tree structure. It supports features like selection, status indicators, execution details, and RTL/LTR text direction.
+
+import { DocsPage } from "@/components/docs-page";
+
+ {
+ const basicTreeData = [
+ {
+ id: '1',
+ name: 'Root Folder',
+ status: 'success',
+ children: [
+ { id: '1-1', name: 'Child File 1', status: 'success' },
+ { id: '1-2', name: 'Child File 2', status: 'running' }
+ ]
+ }
+ ];
+
+return
+
+
+
+ Child File 1
+
+
+ Child File 2
+
+
+
+
+}`}
+/>
+
+## Usage
+
+```typescript jsx
+import { Tree, Folder, File, CollapseButton, type TreeViewElement } from '@harnessio/ui/components'
+import { ExecutionState } from '@harnessio/ui/views'
+
+//...
+
+return (
+
+
+
+ File 1
+
+
+
+)
+```
+
+## Basic Tree Structure
+
+Create a simple tree with folders and files. The tree automatically handles expand/collapse interactions.
+
+ {
+ const simpleTree = [
+ {
+ id: 'folder-1',
+ name: 'Documents',
+ status: 'success',
+ children: [
+ { id: 'file-1', name: 'Resume.pdf', status: 'success' },
+ { id: 'file-2', name: 'Cover Letter.docx', status: 'success' }
+ ]
+ },
+ {
+ id: 'folder-2',
+ name: 'Projects',
+ status: 'running',
+ children: [
+ {
+ id: 'folder-2-1',
+ name: 'Web App',
+ status: 'running',
+ children: [
+ { id: 'file-3', name: 'index.html', status: 'success' },
+ { id: 'file-4', name: 'styles.css', status: 'running' }
+ ]
+ }
+ ]
+ }
+ ];
+
+return
+
+
+
+ Resume.pdf
+
+
+ Cover Letter.docx
+
+
+
+
+
+ index.html
+
+
+ styles.css
+
+
+
+
+
+}`}
+/>
+
+## With Status Indicators
+
+The TreeView component displays status icons based on the `ExecutionState`. It supports states like `SUCCESS`, `RUNNING`, `FAILURE`, `PENDING`, and more.
+
+ {
+ const statusTree = [
+ {
+ id: 'build-1',
+ name: 'Build Pipeline',
+ status: 'running',
+ duration: '2m 30s',
+ children: [
+ { id: 'test-1', name: 'Unit Tests', status: 'success', duration: '45s' },
+ { id: 'test-2', name: 'Integration Tests', status: 'running', duration: '1m 15s' },
+ { id: 'test-3', name: 'E2E Tests', status: 'pending' }
+ ]
+ },
+ {
+ id: 'build-2',
+ name: 'Deploy Pipeline',
+ status: 'failure',
+ duration: '3m 10s',
+ children: [
+ { id: 'deploy-1', name: 'Staging Deploy', status: 'success', duration: '2m' },
+ { id: 'deploy-2', name: 'Production Deploy', status: 'failure', duration: '1m 10s' }
+ ]
+ }
+ ];
+
+return
+
+
+
+ Unit Tests
+
+
+ Integration Tests
+
+
+ E2E Tests
+
+
+
+
+ Staging Deploy
+
+
+ Production Deploy
+
+
+
+
+}`}
+/>
+
+## With Initial Selection
+
+You can specify an initial selected item using the `initialSelectedId` prop. The tree will automatically expand parent folders to show the selected item.
+
+ {
+ const selectionTree = [
+ {
+ id: 'root',
+ name: 'Root',
+ status: 'success',
+ children: [
+ {
+ id: 'nested-folder',
+ name: 'Nested Folder',
+ status: 'success',
+ children: [
+ { id: 'target-file', name: 'Target File (Selected)', status: 'success' },
+ { id: 'other-file', name: 'Other File', status: 'success' }
+ ]
+ }
+ ]
+ }
+ ];
+
+return
+
+
+
+
+ Target File (Selected)
+
+
+ Other File
+
+
+
+
+
+}`}
+/>
+
+## With Initial Expanded Items
+
+Control which folders are initially expanded using the `initialExpendedItems` prop.
+
+ {
+ const expandedTree = [
+ {
+ id: 'folder-a',
+ name: 'Folder A (Expanded)',
+ status: 'success',
+ children: [
+ { id: 'file-a1', name: 'File A1', status: 'success' },
+ { id: 'file-a2', name: 'File A2', status: 'success' }
+ ]
+ },
+ {
+ id: 'folder-b',
+ name: 'Folder B (Collapsed)',
+ status: 'success',
+ children: [
+ { id: 'file-b1', name: 'File B1', status: 'success' }
+ ]
+ }
+ ];
+
+return
+
+
+ File A1
+ File A2
+
+
+ File B1
+
+
+
+}`}
+/>
+
+## Non-Selectable Items
+
+You can make specific items non-selectable by setting `isSelectable: false` on the element.
+
+ {
+ const nonSelectableTree = [
+ {
+ id: 'config',
+ name: 'Configuration (Non-selectable)',
+ status: 'success',
+ isSelectable: false,
+ children: [
+ { id: 'config-1', name: 'app.config.js', status: 'success' },
+ { id: 'config-2', name: 'env.config.js', status: 'success' }
+ ]
+ },
+ {
+ id: 'src',
+ name: 'Source Files',
+ status: 'success',
+ children: [
+ { id: 'src-1', name: 'index.ts', status: 'success' }
+ ]
+ }
+ ];
+
+return
+
+
+
+ app.config.js
+
+
+ env.config.js
+
+
+
+
+ index.ts
+
+
+
+
+}`}
+/>
+
+## With Collapse Button
+
+Add a collapse/expand all button using the `CollapseButton` component.
+
+ {
+ const collapseTreeElements = [
+ {
+ id: 'module-1',
+ name: 'Module 1',
+ status: 'success',
+ children: [
+ { id: 'file-1', name: 'component.tsx', status: 'success' },
+ { id: 'file-2', name: 'styles.css', status: 'success' }
+ ]
+ },
+ {
+ id: 'module-2',
+ name: 'Module 2',
+ status: 'success',
+ children: [
+ { id: 'file-3', name: 'utils.ts', status: 'success' },
+ { id: 'file-4', name: 'types.ts', status: 'success' }
+ ]
+ }
+ ];
+
+return
+
+
+
+
+ component.tsx
+
+
+ styles.css
+
+
+
+
+ utils.ts
+
+
+ types.ts
+
+
+
+
+
+
+
+
+}`}
+/>
+
+## RTL Support
+
+The TreeView component supports right-to-left (RTL) text direction using the `dir` prop.
+
+ {
+ const rtlTree = [
+ {
+ id: 'rtl-1',
+ name: 'مجلد رئيسي',
+ status: 'success',
+ children: [
+ { id: 'rtl-1-1', name: 'ملف 1', status: 'success' },
+ { id: 'rtl-1-2', name: 'ملف 2', status: 'running' }
+ ]
+ }
+ ];
+
+return
+
+
+
+ ملف 1
+
+
+ ملف 2
+
+
+
+
+}`}
+/>
+
+## Complex Example - Full-Featured Pipeline Execution Tree
+
+This comprehensive example demonstrates all features of the TreeView component including:
+
+- Multiple nested levels
+- Various execution states with status icons
+- Duration display for each item
+- Initial selection and expansion
+- Collapse/expand all functionality
+- Mix of selectable and non-selectable items
+- Real-world CI/CD pipeline structure
+
+ {
+ const pipelineExecutionTree = [
+ {
+ id: 'pipeline-main',
+ name: 'Main Pipeline',
+ status: 'running',
+ duration: '15m 45s',
+ children: [
+ {
+ id: 'stage-build',
+ name: 'Build Stage',
+ status: 'success',
+ duration: '5m 20s',
+ children: [
+ {
+ id: 'job-compile',
+ name: 'Compile',
+ status: 'success',
+ duration: '2m 15s',
+ children: [
+ { id: 'step-install', name: 'Install Dependencies', status: 'success', duration: '45s' },
+ { id: 'step-compile', name: 'Run Compiler', status: 'success', duration: '1m 30s' }
+ ]
+ },
+ {
+ id: 'job-lint',
+ name: 'Lint & Format',
+ status: 'success',
+ duration: '1m 30s',
+ children: [
+ { id: 'step-eslint', name: 'ESLint Check', status: 'success', duration: '50s' },
+ { id: 'step-prettier', name: 'Prettier Check', status: 'success', duration: '40s' }
+ ]
+ },
+ {
+ id: 'job-build',
+ name: 'Build Application',
+ status: 'success',
+ duration: '1m 35s'
+ }
+ ]
+ },
+ {
+ id: 'stage-test',
+ name: 'Test Stage',
+ status: 'running',
+ duration: '8m 15s',
+ children: [
+ {
+ id: 'job-unit',
+ name: 'Unit Tests',
+ status: 'success',
+ duration: '3m 20s',
+ children: [
+ { id: 'step-jest', name: 'Jest Tests', status: 'success', duration: '2m 45s' },
+ { id: 'step-coverage', name: 'Coverage Report', status: 'success', duration: '35s' }
+ ]
+ },
+ {
+ id: 'job-integration',
+ name: 'Integration Tests',
+ status: 'running',
+ duration: '4m 55s',
+ children: [
+ { id: 'step-setup-db', name: 'Setup Test DB', status: 'success', duration: '1m 10s' },
+ { id: 'step-api-tests', name: 'API Tests', status: 'running', duration: '2m 30s' },
+ { id: 'step-cleanup', name: 'Cleanup', status: 'pending' }
+ ]
+ },
+ {
+ id: 'job-e2e',
+ name: 'E2E Tests',
+ status: 'pending',
+ isSelectable: false
+ }
+ ]
+ },
+ {
+ id: 'stage-security',
+ name: 'Security Scan',
+ status: 'pending',
+ isSelectable: false,
+ children: [
+ { id: 'job-sast', name: 'SAST Scan', status: 'pending' },
+ { id: 'job-dependency', name: 'Dependency Check', status: 'pending' }
+ ]
+ },
+ {
+ id: 'stage-deploy',
+ name: 'Deploy Stage',
+ status: 'pending',
+ isSelectable: false,
+ children: [
+ {
+ id: 'job-staging',
+ name: 'Deploy to Staging',
+ status: 'pending',
+ children: [
+ { id: 'step-build-image', name: 'Build Docker Image', status: 'pending' },
+ { id: 'step-push-registry', name: 'Push to Registry', status: 'pending' },
+ { id: 'step-deploy-k8s', name: 'Deploy to K8s', status: 'pending' }
+ ]
+ },
+ {
+ id: 'job-production',
+ name: 'Deploy to Production',
+ status: 'pending',
+ isSelectable: false
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: 'pipeline-hotfix',
+ name: 'Hotfix Pipeline',
+ status: 'failure',
+ duration: '4m 12s',
+ children: [
+ {
+ id: 'hotfix-build',
+ name: 'Quick Build',
+ status: 'success',
+ duration: '1m 45s'
+ },
+ {
+ id: 'hotfix-test',
+ name: 'Smoke Tests',
+ status: 'failure',
+ duration: '2m 27s',
+ children: [
+ { id: 'hotfix-test-1', name: 'Critical Path Test', status: 'failure', duration: '2m 27s' }
+ ]
+ }
+ ]
+ },
+ {
+ id: 'pipeline-scheduled',
+ name: 'Scheduled Maintenance',
+ status: 'skipped',
+ duration: '--',
+ children: [
+ { id: 'cleanup-logs', name: 'Cleanup Logs', status: 'skipped' },
+ { id: 'backup-db', name: 'Backup Database', status: 'skipped' }
+ ]
+ }
+ ];
+
+return
+
+
+
+
+
+
+ Install Dependencies
+
+
+ Run Compiler
+
+
+
+
+ ESLint Check
+
+
+ Prettier Check
+
+
+
+ Build Application
+
+
+
+
+
+ Jest Tests
+
+
+ Coverage Report
+
+
+
+
+ Setup Test DB
+
+
+ API Tests
+
+
+ Cleanup
+
+
+
+ E2E Tests
+
+
+
+
+ SAST Scan
+
+
+ Dependency Check
+
+
+
+
+
+ Build Docker Image
+
+
+ Push to Registry
+
+
+ Deploy to K8s
+
+
+
+ Deploy to Production
+
+
+
+
+
+ Quick Build
+
+
+
+ Critical Path Test
+
+
+
+
+
+ Cleanup Logs
+
+
+ Backup Database
+
+
+
+
+
+
+
+
+}`}
+/>
+
+## API Reference
+
+### TreeViewElement Type
+
+```typescript
+type TreeViewElement = {
+ id: string; // Unique identifier for the tree item
+ name: string; // Display name of the item
+ isSelectable?: boolean; // Whether the item can be selected (default: true)
+ children?: TreeViewElement[]; // Nested child elements
+ status: ExecutionState; // Execution status (see ExecutionState enum)
+ duration?: string; // Formatted duration string (e.g., "2m 30s")
+};
+```
+
+### ExecutionState Enum
+
+```typescript
+enum ExecutionState {
+ PENDING = "pending",
+ RUNNING = "running",
+ SUCCESS = "success",
+ FAILURE = "failure",
+ ERROR = "error",
+ SKIPPED = "skipped",
+ KILLED = "killed",
+ BLOCKED = "blocked",
+ WAITING_ON_DEPENDENCIES = "waiting_on_dependencies",
+ UNKNOWN = "unknown",
+}
+```
+
+### Tree Component Props
+
+
+
+### Folder Component Props
+
+
+
+### File Component Props
+
+ void",
+ },
+ ]}
+/>
+
+### CollapseButton Component Props
+
+
+
+## Helper Utility: Rendering Tree Elements
+
+You can create a helper utility to recursively render tree elements from data:
+
+```typescript jsx
+import { ReactNode } from 'react'
+import { File, Folder, type TreeViewElement } from '@harnessio/ui/components'
+
+interface RenderTreeElementProps {
+ element: TreeViewElement
+ handleClick?: (params: { parentNode: TreeViewElement | null; childNode: TreeViewElement }) => void
+ parentElement?: TreeViewElement | null
+ level?: number
+}
+
+// Main render function
+export const renderTree = (
+ elements: TreeViewElement[],
+ handleClick?: (params: { parentNode: TreeViewElement | null; childNode: TreeViewElement }) => void
+): ReactNode => {
+ if (elements.length === 0) return []
+ return elements.map(element => (
+
+ {renderTreeElement({ element, handleClick, parentElement: null })}
+
+ ))
+}
+
+// Render folder with children
+const renderTreeFolder = ({ element: folderElement, handleClick, level = 0 }: RenderTreeElementProps): ReactNode => {
+ return (
+
+ {folderElement.children?.map(childElement => (
+
+ {renderTreeElement({
+ parentElement: folderElement,
+ element: childElement,
+ handleClick,
+ level: level + 1
+ })}
+
+ ))}
+
+ )
+}
+
+// Render file (leaf node)
+const renderTreeFile = ({
+ element: fileElement,
+ handleClick,
+ parentElement,
+ level = 0
+}: RenderTreeElementProps): ReactNode => {
+ return (
+ handleClick?.({ parentNode: parentElement, childNode: fileElement })}
+ level={level}
+ >
+ {fileElement.name}
+
+ )
+}
+
+// Decide whether to render folder or file
+const renderTreeElement = ({
+ element,
+ handleClick,
+ parentElement,
+ level = 0
+}: RenderTreeElementProps): ReactNode => {
+ if (element.children && element.children.length > 0) {
+ return renderTreeFolder({ element, handleClick, parentElement, level })
+ }
+ return renderTreeFile({ element, handleClick, parentElement, level })
+}
+
+// Usage example:
+const treeData: TreeViewElement[] = [
+ {
+ id: '1',
+ name: 'Folder',
+ status: 'success',
+ children: [
+ { id: '1-1', name: 'File 1', status: 'success' }
+ ]
+ }
+]
+
+
+ {renderTree(treeData, ({ parentNode, childNode }) => {
+ console.log('Selected:', childNode.name)
+ })}
+
+```
diff --git a/apps/portal/src/content/docs/foundations/typography.mdx b/apps/portal/src/content/docs/foundations/typography.mdx
index 4a6e552500..480730f5d6 100644
--- a/apps/portal/src/content/docs/foundations/typography.mdx
+++ b/apps/portal/src/content/docs/foundations/typography.mdx
@@ -94,8 +94,8 @@ Captions are used for auxiliary information, labels, and metadata.
Consistency in typography helps users navigate content with ease.
- caption-single-line-soft:
- Consistency in typography helps users navigate content with ease.
+ caption-single-line-light:
+ Consistency in typography helps users navigate content with ease.
`}
/>
diff --git a/packages/core-design-system/design-tokens/components/desktop/base/drawer.json b/packages/core-design-system/design-tokens/components/desktop/base/drawer.json
index fcf5a43c6d..caa3681672 100644
--- a/packages/core-design-system/design-tokens/components/desktop/base/drawer.json
+++ b/packages/core-design-system/design-tokens/components/desktop/base/drawer.json
@@ -41,4 +41,4 @@
"$value": "{size.8}"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/core-design-system/design-tokens/mode/dark/default-deuteranopia.json b/packages/core-design-system/design-tokens/mode/dark/default-deuteranopia.json
index 997f042524..50d779e0fc 100644
--- a/packages/core-design-system/design-tokens/mode/dark/default-deuteranopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/default-deuteranopia.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/default-protanopia.json b/packages/core-design-system/design-tokens/mode/dark/default-protanopia.json
index 170b772497..cb32351e34 100644
--- a/packages/core-design-system/design-tokens/mode/dark/default-protanopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/default-protanopia.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/default-tritanopia.json b/packages/core-design-system/design-tokens/mode/dark/default-tritanopia.json
index b973d8fa73..19bcd29664 100644
--- a/packages/core-design-system/design-tokens/mode/dark/default-tritanopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/default-tritanopia.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/default.json b/packages/core-design-system/design-tokens/mode/dark/default.json
index ac737365bf..5631764cfa 100644
--- a/packages/core-design-system/design-tokens/mode/dark/default.json
+++ b/packages/core-design-system/design-tokens/mode/dark/default.json
@@ -176,17 +176,17 @@
},
"bg": {
"$type": "color",
- "$value": "{blue.950}",
+ "$value": "{blue.900}",
"$description": "Background color for subdued gray surfaces."
},
"bg-hover": {
"$type": "color",
- "$value": "{blue.900}",
+ "$value": "{blue.850}",
"$description": "Hover state background color for interactive brand subtle surfaces."
},
"bg-selected": {
"$type": "color",
- "$value": "{blue.900}",
+ "$value": "{blue.850}",
"$description": "Selected state background color for active brand subtle items."
}
},
@@ -198,7 +198,7 @@
},
"bg": {
"$type": "color",
- "$value": "{blue.900}",
+ "$value": "{blue.1000}",
"$description": "Background color for primary brand items with minimum emphasis."
},
"border": {
@@ -208,12 +208,12 @@
},
"bg-hover": {
"$type": "color",
- "$value": "{blue.800}",
+ "$value": "{blue.950}",
"$description": "Hover state background color for interactive brand with minimum emphasis surfaces."
},
"bg-selected": {
"$type": "color",
- "$value": "{blue.800}",
+ "$value": "{blue.900}",
"$description": "Selected state background color for active brand with minimum emphasis items."
}
}
@@ -2085,7 +2085,7 @@
"y": "0",
"blur": "0",
"spread": "4",
- "color": "{set.brand.outline.bg}",
+ "color": "{set.brand.secondary.bg}",
"type": "dropShadow"
}
},
@@ -2804,7 +2804,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/dimmer-deuteranopia.json b/packages/core-design-system/design-tokens/mode/dark/dimmer-deuteranopia.json
index 2b3bc3de79..b0b350f33c 100644
--- a/packages/core-design-system/design-tokens/mode/dark/dimmer-deuteranopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/dimmer-deuteranopia.json
@@ -2767,7 +2767,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/dimmer-protanopia.json b/packages/core-design-system/design-tokens/mode/dark/dimmer-protanopia.json
index b331a1c1b7..a2d754772e 100644
--- a/packages/core-design-system/design-tokens/mode/dark/dimmer-protanopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/dimmer-protanopia.json
@@ -2767,7 +2767,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/dimmer-tritanopia.json b/packages/core-design-system/design-tokens/mode/dark/dimmer-tritanopia.json
index 9eb2a2c026..795cfeb58b 100644
--- a/packages/core-design-system/design-tokens/mode/dark/dimmer-tritanopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/dimmer-tritanopia.json
@@ -2767,7 +2767,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/dimmer.json b/packages/core-design-system/design-tokens/mode/dark/dimmer.json
index 893ad03e41..f15064750f 100644
--- a/packages/core-design-system/design-tokens/mode/dark/dimmer.json
+++ b/packages/core-design-system/design-tokens/mode/dark/dimmer.json
@@ -2767,7 +2767,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/high-contrast-deuteranopia.json b/packages/core-design-system/design-tokens/mode/dark/high-contrast-deuteranopia.json
index 5b96bba08c..f7313753ec 100644
--- a/packages/core-design-system/design-tokens/mode/dark/high-contrast-deuteranopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/high-contrast-deuteranopia.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/high-contrast-protanopia.json b/packages/core-design-system/design-tokens/mode/dark/high-contrast-protanopia.json
index 025d872f8b..1add5b5c42 100644
--- a/packages/core-design-system/design-tokens/mode/dark/high-contrast-protanopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/high-contrast-protanopia.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/high-contrast-tritanopia.json b/packages/core-design-system/design-tokens/mode/dark/high-contrast-tritanopia.json
index e280144965..c10f126254 100644
--- a/packages/core-design-system/design-tokens/mode/dark/high-contrast-tritanopia.json
+++ b/packages/core-design-system/design-tokens/mode/dark/high-contrast-tritanopia.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/dark/high-contrast.json b/packages/core-design-system/design-tokens/mode/dark/high-contrast.json
index 6527dfcf0c..1002f0cfc6 100644
--- a/packages/core-design-system/design-tokens/mode/dark/high-contrast.json
+++ b/packages/core-design-system/design-tokens/mode/dark/high-contrast.json
@@ -2776,7 +2776,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/default-deuteranopia.json b/packages/core-design-system/design-tokens/mode/light/default-deuteranopia.json
index 567a91c650..bbf52be9b8 100644
--- a/packages/core-design-system/design-tokens/mode/light/default-deuteranopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/default-deuteranopia.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/default-protanopia.json b/packages/core-design-system/design-tokens/mode/light/default-protanopia.json
index 906487379c..ab083b93ce 100644
--- a/packages/core-design-system/design-tokens/mode/light/default-protanopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/default-protanopia.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/default-tritanopia.json b/packages/core-design-system/design-tokens/mode/light/default-tritanopia.json
index d1c3eef442..36fdfdeda5 100644
--- a/packages/core-design-system/design-tokens/mode/light/default-tritanopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/default-tritanopia.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/default.json b/packages/core-design-system/design-tokens/mode/light/default.json
index 9243b7e2ec..64a806a9f9 100644
--- a/packages/core-design-system/design-tokens/mode/light/default.json
+++ b/packages/core-design-system/design-tokens/mode/light/default.json
@@ -2076,7 +2076,7 @@
"y": "0",
"blur": "0",
"spread": "4",
- "color": "{set.brand.outline.bg}",
+ "color": "{set.brand.secondary.bg}",
"type": "dropShadow"
}
},
@@ -2804,7 +2804,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/dimmer-deuteranopia.json b/packages/core-design-system/design-tokens/mode/light/dimmer-deuteranopia.json
index 067e475448..cac8851baa 100644
--- a/packages/core-design-system/design-tokens/mode/light/dimmer-deuteranopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/dimmer-deuteranopia.json
@@ -2756,7 +2756,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/dimmer-protanopia.json b/packages/core-design-system/design-tokens/mode/light/dimmer-protanopia.json
index 5850228a6f..6191371820 100644
--- a/packages/core-design-system/design-tokens/mode/light/dimmer-protanopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/dimmer-protanopia.json
@@ -2756,7 +2756,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/dimmer-tritanopia.json b/packages/core-design-system/design-tokens/mode/light/dimmer-tritanopia.json
index 80bbea6ea3..f465fe54f5 100644
--- a/packages/core-design-system/design-tokens/mode/light/dimmer-tritanopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/dimmer-tritanopia.json
@@ -2756,7 +2756,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/dimmer.json b/packages/core-design-system/design-tokens/mode/light/dimmer.json
index 99e5d46219..64a57d647b 100644
--- a/packages/core-design-system/design-tokens/mode/light/dimmer.json
+++ b/packages/core-design-system/design-tokens/mode/light/dimmer.json
@@ -2738,7 +2738,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/high-contrast-deuteranopia.json b/packages/core-design-system/design-tokens/mode/light/high-contrast-deuteranopia.json
index 6d611f7c31..e00b74beae 100644
--- a/packages/core-design-system/design-tokens/mode/light/high-contrast-deuteranopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/high-contrast-deuteranopia.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/high-contrast-protanopia.json b/packages/core-design-system/design-tokens/mode/light/high-contrast-protanopia.json
index 35bc6f75be..a486ca51d5 100644
--- a/packages/core-design-system/design-tokens/mode/light/high-contrast-protanopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/high-contrast-protanopia.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/high-contrast-tritanopia.json b/packages/core-design-system/design-tokens/mode/light/high-contrast-tritanopia.json
index a2b0560a39..9e6e014f4f 100644
--- a/packages/core-design-system/design-tokens/mode/light/high-contrast-tritanopia.json
+++ b/packages/core-design-system/design-tokens/mode/light/high-contrast-tritanopia.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/core-design-system/design-tokens/mode/light/high-contrast.json b/packages/core-design-system/design-tokens/mode/light/high-contrast.json
index 0fb5cfaae6..1f1df130fd 100644
--- a/packages/core-design-system/design-tokens/mode/light/high-contrast.json
+++ b/packages/core-design-system/design-tokens/mode/light/high-contrast.json
@@ -2747,7 +2747,7 @@
"disabled": {
"opacity": {
"$type": "opacity",
- "$value": "{opacity.50}"
+ "$value": "{opacity.60}"
}
},
"img": {
diff --git a/packages/forms/package.json b/packages/forms/package.json
index 3d4d687dfe..dacdc2be32 100644
--- a/packages/forms/package.json
+++ b/packages/forms/package.json
@@ -1,6 +1,6 @@
{
"name": "@harnessio/forms",
- "version": "0.1.3",
+ "version": "0.1.5",
"description": "Harness Forms Library",
"scripts": {
"playground": "vite dev --config vite.config.playground.ts",
diff --git a/packages/forms/src/core/utils/transform-utils.tsx b/packages/forms/src/core/utils/transform-utils.tsx
index 137c339772..b87d07a015 100644
--- a/packages/forms/src/core/utils/transform-utils.tsx
+++ b/packages/forms/src/core/utils/transform-utils.tsx
@@ -1,6 +1,7 @@
import { cloneDeep, get, isArray, pick, set, unset } from 'lodash-es'
import { IFormDefinition, IInputDefinition } from '../../types/types'
+import { removeTemporaryFieldsValue } from './temporary-field-utils'
type TransformItem = {
path: string
@@ -101,13 +102,20 @@ export function getTransformers(formDefinition: IFormDefinition): TransformItem[
export function unsetHiddenInputsValues(
formDefinition: IFormDefinition,
values: Record
,
- metadata?: any
+ metadata?: any,
+ options: { preserve?: string[] } = {}
): Record {
+ const { preserve } = options
+
const flattenInputs = flattenInputsRec(formDefinition.inputs)
const retValues = cloneDeep(values)
flattenInputs.forEach(input => {
+ if (preserve && preserve.includes(input.path)) {
+ return
+ }
+
if (!!input.isVisible && !input.isVisible(values, metadata)) {
unset(retValues, input.path)
}
@@ -115,3 +123,26 @@ export function unsetHiddenInputsValues(
return retValues
}
+
+/** Convert data to form model */
+export function convertDataToFormModel(
+ inputData: Record,
+ formDefinition: IFormDefinition
+): Record {
+ const transformers = getTransformers(formDefinition)
+ return inputTransformValues(inputData, transformers)
+}
+
+/** Convert form model to data */
+export function convertFormModelToData(
+ formData: Record,
+ formDefinition: IFormDefinition,
+ metadata?: any,
+ options: { preserve?: string[] } = {}
+): Record {
+ const transformers = getTransformers(formDefinition)
+ let data = unsetHiddenInputsValues(formDefinition, formData, metadata, options)
+ data = outputTransformValues(data, transformers)
+ data = removeTemporaryFieldsValue(data)
+ return data
+}
diff --git a/packages/ui/locales/en/component.json b/packages/ui/locales/en/component.json
index e1fa53442a..f1a25ad660 100644
--- a/packages/ui/locales/en/component.json
+++ b/packages/ui/locales/en/component.json
@@ -99,6 +99,9 @@
},
"filter": {
"delete": "Delete Filter",
+ "search": {
+ "placeholder": "Search"
+ },
"inputPlaceholder": "Filter by...",
"buttonLabel": "Reset filters",
"defaultLabel": "Filter",
@@ -181,10 +184,6 @@
}
},
"cancel": "Cancel",
- "pagination": {
- "previous": "Prev",
- "next": "Next"
- },
"searchFile": {
"noFile": "No file found."
},
@@ -237,6 +236,10 @@
"notAllowed": "Cannot create tag",
"bypassButton": "Bypass rules and create tag"
},
+ "pagination": {
+ "previous": "Prev",
+ "next": "Next"
+ },
"layout": {
"table": "Table",
"list": "List"
diff --git a/packages/ui/locales/en/views.json b/packages/ui/locales/en/views.json
index 45f9103f56..80aebccb6b 100644
--- a/packages/ui/locales/en/views.json
+++ b/packages/ui/locales/en/views.json
@@ -51,6 +51,21 @@
"name": "Name",
"lastCommit": "Last commit message",
"date": "Date",
+ "targetRepositories": "Target Repositories",
+ "repositories": "Repositories",
+ "selectIncluded": "Select included",
+ "selectExcluded": "Select excluded",
+ "selectReposTitle": "{{type}} repositories",
+ "selectReposDescription": "Choose repositories to {{action}} from this rule",
+ "repository": "Repository",
+ "noRepositoriesAvailable": "No repositories available",
+ "saveSelection": "Save Selection",
+ "noReposSelectedAppliesAll": "If no repositories are selected, the rule applies to all",
+ "patterns": "Patterns",
+ "targetReposCaption": "Match repositories using globstar patterns (e.g.\"repo-*\", \"prod/**\")",
+ "targetReposPlaceholder": "Enter the target repository patterns",
+ "include": "Include",
+ "exclude": "Exclude",
"cloneCredential": "Generate Clone Credential",
"expiration": "Expiration",
"token": "Token",
@@ -125,8 +140,6 @@
"targetPatterns": "Target patterns",
"createRuleCaption": "Match branches using globstar patterns (e.g.”golden”, “feature-*”, “releases/**”)",
"rulePatternPlaceholder": "Enter the target patterns",
- "include": "Include",
- "exclude": "Exclude",
"applyRuleDefaultBranch": "Apply this rule to the default branch",
"bypassList": "Bypass list",
"selectUsers": "Select users",
@@ -191,7 +204,6 @@
"title": "No files yet",
"description": "This repository is empty. Add your first file to get started."
},
- "repositories": "Repositories",
"createRepository": "Create Repository",
"importRepository": "Import Repository",
"importRepositories": "Import Repositories",
@@ -229,6 +241,10 @@
"secretScanningDescription": "Block commits containing secrets like passwords, API keys and tokens.",
"vulnerabilityScanning": "Vulnerability scanning",
"vulnerabilityScanningDescription": "Scan incoming commits for known vulnerabilities.",
+ "detectVulnerabilities": "Detect",
+ "detectVulnerabilitiesDescription": "Passive vulnerability will report errors but not block",
+ "blockVulnerabilities": "Block",
+ "blockVulnerabilitiesDescription": "Active vulnerability blocks commit if any vulnerability is found",
"verifyCommitterIdentity": "Verify committer identity",
"verifyCommitterIdentityDescription": "Block commits not committed by the user pushing the changes.",
"generalSettings": "General settings",
@@ -392,7 +408,6 @@
"clone": "Clone",
"addFile": "Add file"
},
- "search": "Search",
"execution": {
"summary": "Summary",
"logs": "Logs",
@@ -443,8 +458,10 @@
"inviteMembers": "There are no members in this project. Click on the button below to start adding them.",
"noRules": "No rules yet",
"noRulesDescription": "There are no rules in this project. Click on the button below to start adding rules.",
- "noCommitsYet": "No commits yet",
- "noCommitsYetDescription": "Your commits will appear here once they're made. Start committing to see your changes reflected.",
+ "pullRequests": {
+ "noNewCommits": "There isn’t anything to compare",
+ "noNewCommitsDescription": "{{source}} and {{target}} are identical."
+ },
"compareChanges": "Compare and review just about anything",
"compareChangesDescription": "Branches and commit ranges can be reviewed within the same repository.",
"title": {
@@ -464,7 +481,9 @@
"createBranchDescription": "Your branches will appear here once they're created.",
"startBranchDescription": "Start branching to see your work organized.",
"noCommitsHistory": "No commits history",
+ "noCommitsYet": "No commits yet",
"noCommitsHistoryDescription": "There isn't any commit history to show here for the selected user, time range, or current page.",
+ "noCommitsYetDescription": "Your commits will appear here once they're made. Start committing to see your changes reflected.",
"clearFilters": "Clear Filters",
"errorApiTitle": "Failed to load {{type}}",
"errorApiDescription": "An error occurred while loading the data. Please try again and reload the page.",
@@ -561,6 +580,7 @@
"compareChangesDraftTitle": "Create Draft Pull request",
"compareChangesDraftDescription": "Does not request code reviews and cannot be merged.",
"compareChangesCreatedButton": "Pull Request Created",
+ "expandSidebar": "Expand Sidebar",
"compareChangesFormTitleLabel": "Add a title",
"compareChangesFormTitlePlaceholder": "Enter pull request title",
"compareChangesFormDescriptionHeading": "Add a description",
@@ -579,7 +599,6 @@
"compareChangesCantMergeDescription": "You can still create the pull request.",
"compareChangesDiscussChanges": "Discuss and review the changes in this comparison with others.",
"compareChangesDiscussChangesLink": "Learn about pull requests.",
- "compareChangesChooseDifferent": "Choose different branches above to discuss and review changes.",
"compareChangesViewPRLink": "View pull request",
"compareChangesTabOverview": "Overview",
"compareChangesTabCommits": "Commits",
@@ -596,6 +615,7 @@
"noLabelsSidebar": "No labels",
"changedSinceLastView": "Changed since last viewed",
"markViewed": "Viewed",
+ "collapseSidebar": "Collapse Sidebar",
"deletedComment": "This comment was deleted.",
"reviewers": "Reviewers",
"noUsers": "No users found.",
@@ -611,6 +631,7 @@
"suggestionApplied": "Suggestion applied",
"codeSuggestion": "Code suggestion"
},
+ "compareChangesChooseDifferent": "Choose different branches above to discuss and review changes.",
"deleted": "Deleted",
"searchUsers": "Search users",
"compareChangesFormDescriptionLabel": "Description",
@@ -634,10 +655,14 @@
},
"secretDetails": {
"backToSecrets": "Back to secrets",
+ "created": "Created",
+ "updated": "Updated",
+ "lastUsed": "Last used",
"configuration": "Configuration",
"references": "References",
"activity": "Activity",
- "configurationView": "Secret Configuration"
+ "configurationView": "Secret Configuration",
+ "lastUpdated": "Last updated"
},
"notFound": {
"title": "Something went wrong…",
@@ -793,6 +818,81 @@
"commitDetailsDiffWith": "with",
"commitDetailsDiffAdditionsAnd": "additions and",
"commitDetailsDiffDeletions": "deletions"
+ },
+ "pullRequest": {
+ "codeOwnersSection": {
+ "requestedChanges": "Code owners requested changes to the pull request",
+ "waitingOnReviews": "Waiting on code owner reviews of latest changes",
+ "changesPendingApproval": "Changes are pending approval from code owners",
+ "someChangesApproved": "Some changes were approved by code owners",
+ "latestChangesApproved": "Latest changes were approved by code owners",
+ "changesApproved": "Changes were approved by code owners",
+ "latestChangesPendingApproval": "Latest changes are pending approval from required reviewers",
+ "noReviewsRequired": "No codeowner reviews required",
+ "code": "Code",
+ "owners": "Owners",
+ "changesRequestedBy": "Changes requested by",
+ "approvedBy": "Approved by"
+ },
+ "requiredMessage": "Required",
+ "defaultReviewersSection": {
+ "waitingOnReviews": "Waiting on default reviewer's reviews of latest changes",
+ "changesPendingApproval": "Changes are pending approval from default reviewers",
+ "latestChangesApproved": "Latest changes were approved by default reviewers",
+ "changesApproved": "Changes were approved by default reviewers",
+ "defaultReviewersAdded": "Default reviewers were added to the PR",
+ "defaultReviewers": "Default reviewers",
+ "changesRequestedBy": "Changes requested by",
+ "approvedBy": "Approved by"
+ },
+ "changesSection": {
+ "approvalsMessage": "Changes were approved by {{approvalsCount}} {{approvedBy}}",
+ "latestApprovalsMessage": "Latest changes were approved by {{latestApprovalsCount}} {{approvedBy}}",
+ "changeRequestedMessage": "{{reviewer}} requested changes to the pull request",
+ "pendingApprovalsMessage": "{{approvalsCount}}/{{minApproval}} approvals completed",
+ "pendingLatestApprovalsMessage": "{{latestApprovalsCount}} {{approval}} pending on latest changes"
+ },
+ "checksSection": {
+ "succeededMessage": "Succeeded in {{time}}",
+ "failedMessage": "Failed in {{time}}",
+ "runningMessage": "Running...",
+ "pendingMessage": "Pending...",
+ "erroredMessage": "Errored in {{time}}",
+ "detailsLink": "Details"
+ },
+ "commentSection": {
+ "view": "View"
+ },
+ "mergeSection": {
+ "step": "Step {{number}}.",
+ "step1": "Clone the repository or update your local repository with the latest changes",
+ "step2": "Switch to the head branch of the pull request",
+ "step3": "Merge the base branch into the head branch",
+ "step4": "Fix the conflicts and commit the results",
+ "comment": {
+ "1": "See ",
+ "2": "Resolving a merge conflict using the command line",
+ "3": "for step-by-step instructions on resolving merge conflicts"
+ },
+ "step5": "Push the changes",
+ "mergeCheck": "Merge check in progress...",
+ "conflictsFound": "Conflicts found in this branch",
+ "noConflicts": "This branch has no conflicts with {{branch}} branch",
+ "automaticMergeCheck": "Checking for ability to merge automatically...",
+ "resolveConflicts": {
+ "1": "Use the ",
+ "2": "command line",
+ "3": " to resolve conflicts"
+ },
+ "resolveConflictsCommandLine": "Resolve conflicts via command line",
+ "conflictingFiles": "Conflicting files ",
+ "mergeLatestChangesTitle": "This branch is out-of-date with the base branch",
+ "mergeLatestChanges": {
+ "1": "Merge the latest changes from",
+ "2": "into"
+ },
+ "updateWithRebase": "Update with rebase"
+ }
}
},
"commits": {
@@ -811,6 +911,7 @@
"connectors": {
"filterOptions": {
"statusOption": {
+ "pinned": "Pinned",
"favorite": "Favorites",
"success": "Success",
"failure": "Failure",
@@ -869,6 +970,25 @@
"delete": "Delete Webhook",
"create": "New Webhook"
},
+ "search": {
+ "emptyState": {
+ "title": "Start searching",
+ "description": "Enter search terms to find relevant results,"
+ },
+ "showLess": "- Show Less",
+ "showMore": "+ Show More",
+ "noResultsFound": "No search results found",
+ "startSearching": "Start searching",
+ "tryDifferentQuery": "Try a different search query or clear filters",
+ "enterSearchTerms": "Enter search terms to find relevant results",
+ "searchPlaceholder": "Search anything...",
+ "scopePlaceholder": "Select a scope",
+ "repositoryPlaceholder": "Select a repository",
+ "statsText": {
+ "matchesAndFiles": "{{matchCount}} matches found in {{fileCount}} file(s)",
+ "files": "{{fileCount}} file(s)"
+ }
+ },
"secretActivity": {
"event": "Event",
"entity": "Entity",
@@ -876,13 +996,10 @@
"timestamp": "Timestamp",
"type": "Type"
},
- "entityReference": {
- "entity": "Entity",
- "type": "Type",
- "scope": "Scope",
- "created": "Created"
- },
"secrets": {
+ "search": {
+ "placeholder": "Search"
+ },
"filterOptions": {
"secretType": {
"label": "Secret Type"
@@ -894,28 +1011,38 @@
"descriptionOption": {
"label": "Description"
},
+ "tagsOption": {
+ "label": "Tags"
+ },
"typeOption": {
"label": "Type"
}
},
"secretsTitle": "Secrets",
"createNew": "Create Secret",
- "edit": "Edit Secret",
- "delete": "Delete Secret",
+ "edit": "Edit secret",
+ "delete": "Delete secret",
"newSecret": "New Secret"
},
+ "entityReference": {
+ "entity": "Entity",
+ "type": "Type",
+ "scope": "Scope",
+ "created": "Created"
+ },
"secret": {
"title": "Secret"
},
"common": {
"manager": "Manager",
+ "created": "Created",
+ "updated": "Updated",
"lastActivity": "Last Activity",
"details": "Details",
"overview": "Overview",
"type": "Type",
"createdOn": "Created on",
- "modifiedOn": "Modified on",
- "created": "Created"
+ "modifiedOn": "Modified on"
},
"userManagement": {
"createUser": {
diff --git a/packages/ui/locales/fr/component.json b/packages/ui/locales/fr/component.json
index 9c7a76a46f..b0869eb791 100644
--- a/packages/ui/locales/fr/component.json
+++ b/packages/ui/locales/fr/component.json
@@ -98,6 +98,9 @@
},
"filter": {
"delete": "Delete filter",
+ "search": {
+ "placeholder": "Search"
+ },
"inputPlaceholder": "Filtrer par...",
"buttonLabel": "Réinitialiser les filtres",
"defaultLabel": "Filtrer",
@@ -180,10 +183,6 @@
}
},
"cancel": "Cancel",
- "pagination": {
- "previous": "Précédent",
- "next": "Suivant"
- },
"searchFile": {
"noFile": "Aucun fichier trouvé."
},
@@ -237,6 +236,10 @@
"notAllowed": "Cannot create tag",
"bypassButton": "Bypass rules and create tag"
},
+ "pagination": {
+ "previous": "Précédent",
+ "next": "Suivant"
+ },
"layout": {
"table": "Table",
"list": "List"
diff --git a/packages/ui/locales/fr/views.json b/packages/ui/locales/fr/views.json
index c63c870dcd..a2e8d95197 100644
--- a/packages/ui/locales/fr/views.json
+++ b/packages/ui/locales/fr/views.json
@@ -51,6 +51,21 @@
"name": "Nom",
"lastCommit": "Dernier commit",
"date": "Date",
+ "targetRepositories": "Target Repositories",
+ "repositories": "Dépôts",
+ "selectIncluded": "Select included",
+ "selectExcluded": "Select excluded",
+ "selectReposTitle": "Dépôts {{type}}",
+ "selectReposDescription": "Choisissez les dépôts à {{action}} de cette règle",
+ "repository": "Dépôt",
+ "noRepositoriesAvailable": "No repositories available",
+ "saveSelection": "Save Selection",
+ "noReposSelectedAppliesAll": "If no repositories are selected, the rule applies to all",
+ "patterns": "Patterns",
+ "targetReposCaption": "Match repositories using globstar patterns (e.g.\"repo-*\", \"prod/**\")",
+ "targetReposPlaceholder": "Enter the target repository patterns",
+ "include": "Include",
+ "exclude": "Exclude",
"cloneCredential": "Générer un identifiant de clonage",
"expiration": "Expiration",
"token": "Jeton",
@@ -125,8 +140,6 @@
"targetPatterns": "Modèles cibles",
"createRuleCaption": "Définir les branches correspondant aux modèles globaux (ex : 'feature-*', 'release/**')",
"rulePatternPlaceholder": "Enter the target patterns",
- "include": "Include",
- "exclude": "Exclude",
"applyRuleDefaultBranch": "Appliquer la règle à la branche par défaut",
"bypassList": "Liste de contournement",
"selectUsers": "Sélectionner les utilisateurs",
@@ -191,7 +204,6 @@
"title": "No files yet",
"description": "This repository is empty. Add your first file to get started."
},
- "repositories": "Dépôts",
"createRepository": "Créer un dépôt",
"importRepository": "Importer un dépôt",
"importRepositories": "Import repositories",
@@ -228,9 +240,13 @@
"secretScanning": "Analyse des secrets",
"secretScanningDescription": "Empêcher les commits contenant des secrets comme les mots de passe ou jetons API.",
"vulnerabilityScanning": "Vulnerability scanning",
- "vulnerabilityScanningDescription": "Scan incoming commits for known vulnerabilities.",
+ "vulnerabilityScanningDescription": "Scanner les commits entrants pour les vulnérabilités connues.",
+ "detectVulnerabilities": "Detect",
+ "detectVulnerabilitiesDescription": "Vulnérabilité passive signalera des erreurs mais ne bloquera pas",
+ "blockVulnerabilities": "Block",
+ "blockVulnerabilitiesDescription": "Une vulnérabilité active bloquera le commit si une vulnérabilité est trouvée.",
"verifyCommitterIdentity": "Verify committer identity",
- "verifyCommitterIdentityDescription": "Block commits not committed by the user pushing the changes.",
+ "verifyCommitterIdentityDescription": "Bloquer les commits non-validés par l'utilisateur qui effectue les modifications.",
"generalSettings": "Paramètres généraux",
"general": "Général",
"labels": "Labels",
@@ -383,7 +399,6 @@
"clone": "Cloner",
"addFile": "Ajouter un fichier"
},
- "search": "Rechercher",
"execution": {
"summary": "Résumé",
"logs": "Journaux",
@@ -434,8 +449,10 @@
"inviteMembers": "",
"noRules": "No rules yet",
"noRulesDescription": "There are no rules in this project. Click on the button below to start adding rules.",
- "noCommitsYet": "No commits yet",
- "noCommitsYetDescription": "Your commits will appear here once they're made. Start committing to see your changes reflected.",
+ "pullRequests": {
+ "noNewCommits": "There isn’t anything to compare",
+ "noNewCommitsDescription": "{{source}} and {{target}} are identical."
+ },
"compareChanges": "Comparez et révisez pratiquement tout",
"compareChangesDescription": "Branches, tags, plages de commits et périodes. Dans le même dépôt et entre les forks.",
"title": {
@@ -455,7 +472,9 @@
"createBranchDescription": "Vos branches apparaîtront ici une fois créées.",
"startBranchDescription": "Commencez à créer des branches pour voir votre travail organisé.",
"noCommitsHistory": "No commits history",
+ "noCommitsYet": "No commits yet",
"noCommitsHistoryDescription": "There isn't any commit history to show here for the selected user, time range, or current page.",
+ "noCommitsYetDescription": "Your commits will appear here once they're made. Start committing to see your changes reflected.",
"clearFilters": "Effacer les filtres",
"errorApiTitle": "Échec du chargement de {{type}}",
"errorApiDescription": "Une erreur s'est produite lors du chargement des données. Veuillez réessayer et recharger la page.",
@@ -552,6 +571,7 @@
"compareChangesDraftTitle": "Créer une pull request en brouillon",
"compareChangesDraftDescription": "Ne demande pas de révision de code et ne peut pas être fusionnée.",
"compareChangesCreatedButton": "Pull request créée",
+ "expandSidebar": "Expand Sidebar",
"compareChangesFormTitleLabel": "Add a title",
"compareChangesFormTitlePlaceholder": "Saisissez le titre de la pull request",
"compareChangesFormDescriptionHeading": "Add a description",
@@ -570,7 +590,6 @@
"compareChangesCantMergeDescription": "Vous pouvez quand même créer la pull request.",
"compareChangesDiscussChanges": "Discutez et révisez les modifications de cette comparaison avec d'autres.",
"compareChangesDiscussChangesLink": "En savoir plus sur les pull requests.",
- "compareChangesChooseDifferent": "Choisissez différentes branches ou forks ci-dessus pour discuter et réviser les modifications.",
"compareChangesViewPRLink": "Voir la pull request",
"compareChangesTabOverview": "Aperçu",
"compareChangesTabCommits": "Commits",
@@ -587,6 +606,7 @@
"noLabelsSidebar": "No labels",
"changedSinceLastView": "Modifié depuis la dernière consultation",
"markViewed": "Vue",
+ "collapseSidebar": "Collapse Sidebar",
"deletedComment": "Ce commentaire a été supprimé.",
"reviewers": "Réviseurs",
"noUsers": "Aucun utilisateur trouvé.",
@@ -602,6 +622,7 @@
"suggestionApplied": "Suggestion applied",
"codeSuggestion": "Code suggestion"
},
+ "compareChangesChooseDifferent": "Choisissez différentes branches ou forks ci-dessus pour discuter et réviser les modifications.",
"deleted": "Supprimé",
"searchUsers": "Rechercher des utilisateurs",
"compareChangesFormDescriptionLabel": "Description",
@@ -621,10 +642,14 @@
},
"secretDetails": {
"backToSecrets": "Back to secrets",
+ "created": "Created",
+ "updated": "Updated",
+ "lastUsed": "Last used",
"configuration": "Configuration",
"references": "References",
"activity": "Activity",
- "configurationView": "Secret Configuration"
+ "configurationView": "Secret Configuration",
+ "lastUpdated": "Last updated"
},
"notFound": {
"title": "Quelque chose s'est mal passé…",
@@ -779,6 +804,81 @@
"commitDetailsDiffWith": "avec",
"commitDetailsDiffAdditionsAnd": "ajouts et",
"commitDetailsDiffDeletions": "suppressions"
+ },
+ "pullRequest": {
+ "codeOwnersSection": {
+ "requestedChanges": "Code owners requested changes to the pull request",
+ "waitingOnReviews": "Waiting on code owner reviews of latest changes",
+ "changesPendingApproval": "Changes are pending approval from code owners",
+ "someChangesApproved": "Some changes were approved by code owners",
+ "latestChangesApproved": "Latest changes were approved by code owners",
+ "changesApproved": "Changes were approved by code owners",
+ "latestChangesPendingApproval": "Latest changes are pending approval from required reviewers",
+ "noReviewsRequired": "No codeowner reviews required",
+ "code": "Code",
+ "owners": "Owners",
+ "changesRequestedBy": "Changes requested by",
+ "approvedBy": "Approved by"
+ },
+ "requiredMessage": "Required",
+ "defaultReviewersSection": {
+ "waitingOnReviews": "Waiting on default reviewer's reviews of latest changes",
+ "changesPendingApproval": "Changes are pending approval from default reviewers",
+ "latestChangesApproved": "Latest changes were approved by default reviewers",
+ "changesApproved": "Changes were approved by default reviewers",
+ "defaultReviewersAdded": "Default reviewers were added to the PR",
+ "defaultReviewers": "Default reviewers",
+ "changesRequestedBy": "Changes requested by",
+ "approvedBy": "Approved by"
+ },
+ "changesSection": {
+ "approvalsMessage": "Changes were approved by {{approvalsCount}} {{approvedBy}}",
+ "latestApprovalsMessage": "Latest changes were approved by {{latestApprovalsCount}} {{approvedBy}}",
+ "changeRequestedMessage": "{{reviewer}} requested changes to the pull request",
+ "pendingApprovalsMessage": "{{approvalsCount}}/{{minApproval}} approvals completed",
+ "pendingLatestApprovalsMessage": "{{latestApprovalsCount}} {{approval}} pending on latest changes"
+ },
+ "checksSection": {
+ "succeededMessage": "Succeeded in {{time}}",
+ "failedMessage": "Failed in {{time}}",
+ "runningMessage": "Running...",
+ "pendingMessage": "Pending...",
+ "erroredMessage": "Errored in {{time}}",
+ "detailsLink": "Details"
+ },
+ "commentSection": {
+ "view": "View"
+ },
+ "mergeSection": {
+ "step": "Step {{number}}.",
+ "step1": "Clone the repository or update your local repository with the latest changes",
+ "step2": "Switch to the head branch of the pull request",
+ "step3": "Merge the base branch into the head branch",
+ "step4": "Fix the conflicts and commit the results",
+ "comment": {
+ "1": "See ",
+ "2": "Resolving a merge conflict using the command line",
+ "3": "for step-by-step instructions on resolving merge conflicts"
+ },
+ "step5": "Push the changes",
+ "mergeCheck": "Merge check in progress...",
+ "conflictsFound": "Conflicts found in this branch",
+ "noConflicts": "This branch has no conflicts with {{branch}} branch",
+ "automaticMergeCheck": "Checking for ability to merge automatically...",
+ "resolveConflicts": {
+ "1": "Use the ",
+ "2": "command line",
+ "3": " to resolve conflicts"
+ },
+ "resolveConflictsCommandLine": "Resolve conflicts via command line",
+ "conflictingFiles": "Conflicting files ",
+ "mergeLatestChangesTitle": "This branch is out-of-date with the base branch",
+ "mergeLatestChanges": {
+ "1": "Merge the latest changes from",
+ "2": "into"
+ },
+ "updateWithRebase": "Update with rebase"
+ }
}
},
"commits": {
@@ -797,6 +897,7 @@
"connectors": {
"filterOptions": {
"statusOption": {
+ "pinned": "Pinned",
"favorite": "Favorites",
"success": "Succès",
"failure": "Échec",
@@ -855,6 +956,25 @@
"delete": "Delete webhook",
"create": "Create webhook"
},
+ "search": {
+ "emptyState": {
+ "title": "Start searching",
+ "description": "Enter search terms to find relevant results,"
+ },
+ "showLess": "- Show Less",
+ "showMore": "+ Show More",
+ "noResultsFound": "No search results found",
+ "startSearching": "Start searching",
+ "tryDifferentQuery": "Try a different search query or clear filters",
+ "enterSearchTerms": "Enter search terms to find relevant results",
+ "searchPlaceholder": "Search anything...",
+ "scopePlaceholder": "Select a scope",
+ "repositoryPlaceholder": "Select a repository",
+ "statsText": {
+ "matchesAndFiles": "{{matchCount}} matches found in {{fileCount}} file(s)",
+ "files": "{{fileCount}} file(s)"
+ }
+ },
"secretActivity": {
"event": "Event",
"entity": "Entity",
@@ -862,13 +982,10 @@
"timestamp": "Timestamp",
"type": "Type"
},
- "entityReference": {
- "entity": "Entity",
- "type": "Type",
- "scope": "Scope",
- "created": "Created"
- },
"secrets": {
+ "search": {
+ "placeholder": "Search"
+ },
"filterOptions": {
"secretType": {
"label": "Secret Type"
@@ -880,28 +997,38 @@
"descriptionOption": {
"label": "Description"
},
+ "tagsOption": {
+ "label": "Tags"
+ },
"typeOption": {
"label": "Type"
}
},
"secretsTitle": "Secrets",
"createNew": "Créer un secret",
- "edit": "Edit Secret",
+ "edit": "Edit secret",
"delete": "Supprimer le secret",
"newSecret": "New Secret"
},
+ "entityReference": {
+ "entity": "Entity",
+ "type": "Type",
+ "scope": "Scope",
+ "created": "Created"
+ },
"secret": {
"title": "Secret"
},
"common": {
"manager": "Manager",
+ "created": "Created",
+ "updated": "Updated",
"lastActivity": "Last Activity",
"details": "Details",
"overview": "Overview",
"type": "Type",
"createdOn": "Created on",
- "modifiedOn": "Modified on",
- "created": "Created"
+ "modifiedOn": "Modified on"
},
"userManagement": {
"createUser": {
diff --git a/packages/ui/package.json b/packages/ui/package.json
index fae3eba0b1..abd6f85d42 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,7 +1,7 @@
{
"name": "@harnessio/ui",
"description": "Harness Canary UI component library",
- "version": "0.0.134",
+ "version": "0.0.156",
"private": false,
"type": "module",
"main": "./dist/index.js",
@@ -77,10 +77,8 @@
"@radix-ui/react-context-menu": "^2.2.4",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
- "@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-menubar": "^1.1.4",
- "@radix-ui/react-navigation-menu": "^1.2.3",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-progress": "^1.1.1",
"@radix-ui/react-radio-group": "^1.2.2",
@@ -126,8 +124,7 @@
"rehype-sanitize": "^6.0.0",
"rehype-video": "^2.0.2",
"remark-breaks": "^4.0.0",
- "simple-icons": "^14.11.1",
- "sonner": "^1.5.0",
+ "sonner": "^1.7.4",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2",
@@ -146,7 +143,6 @@
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.9.0",
"@types/react": "^17.0.3",
- "@types/simple-icons": "^5.21.1",
"@vitejs/plugin-react-swc": "^3.8.0",
"@vitest/coverage-istanbul": "^3.0.6",
"@vitest/ui": "^3.0.6",
diff --git a/packages/ui/src/components/app-sidebar/sidebar-item.tsx b/packages/ui/src/components/app-sidebar/sidebar-item.tsx
index 494e0807a9..db55b0483c 100644
--- a/packages/ui/src/components/app-sidebar/sidebar-item.tsx
+++ b/packages/ui/src/components/app-sidebar/sidebar-item.tsx
@@ -1,4 +1,4 @@
-import { NavbarItemType, Sidebar } from '@/components'
+import { NavbarItemType, Sidebar, SidebarItemProps } from '@/components'
import { useTranslation } from '@/context'
import { wrapConditionalArrayElements } from '@utils/mergeUtils'
@@ -9,6 +9,8 @@ interface NavbarItemProps {
handleRemoveRecentMenuItem: (item: NavbarItemType) => void
handleCustomNav: () => void
disabled?: boolean
+ actionButtons?: SidebarItemProps['actionButtons']
+ hideMenuItems?: boolean
}
export const AppSidebarItem = ({
@@ -17,7 +19,9 @@ export const AppSidebarItem = ({
handleChangePinnedMenuItem,
handleRemoveRecentMenuItem,
handleCustomNav,
- disabled = false
+ disabled = false,
+ actionButtons,
+ hideMenuItems = false
}: NavbarItemProps) => {
const { t } = useTranslation()
@@ -39,12 +43,14 @@ export const AppSidebarItem = ({
return (
)
}
+AppSidebarItem.displayName = 'AppSidebarItem'
diff --git a/packages/ui/src/components/app-sidebar/sidebar-search/command-palette-wrapper.tsx b/packages/ui/src/components/app-sidebar/sidebar-search/command-palette-wrapper.tsx
index 791904d8f3..464e5b9a6d 100644
--- a/packages/ui/src/components/app-sidebar/sidebar-search/command-palette-wrapper.tsx
+++ b/packages/ui/src/components/app-sidebar/sidebar-search/command-palette-wrapper.tsx
@@ -130,12 +130,12 @@ export function CommandPaletteWrapper() {
{
label: 'Canary',
url: '/canary/repos/petstore-app/summary',
- icon: () => renderIcon('page')
+ icon: () => renderIcon('empty-page')
},
{
label: 'Paypal',
url: '/canary/repos/real-world/summary',
- icon: () => renderIcon('page')
+ icon: () => renderIcon('empty-page')
}
],
[PageKey.PIPELINES]: [
diff --git a/packages/ui/src/components/app-sidebar/sidebar-search/sidebar-search.tsx b/packages/ui/src/components/app-sidebar/sidebar-search/sidebar-search.tsx
index 3112d5dcf6..8ce9e9b9d2 100644
--- a/packages/ui/src/components/app-sidebar/sidebar-search/sidebar-search.tsx
+++ b/packages/ui/src/components/app-sidebar/sidebar-search/sidebar-search.tsx
@@ -21,7 +21,7 @@ export function SidebarSearch() {
size="sm"
placeholder={t('component:navbar.search', 'Search')}
className="pointer-events-none"
- inputContainerClassName="border-cn-2 [&>.cn-input-prefix]:w-[34px] max-w-full overflow-hidden"
+ inputContainerClassName="border-cn-2 max-w-full overflow-hidden"
suffix={⌘K }
readOnly
aria-hidden="true"
diff --git a/packages/ui/src/components/app-sidebar/sidebar-user.tsx b/packages/ui/src/components/app-sidebar/sidebar-user.tsx
index e28700ee99..1c9f876444 100644
--- a/packages/ui/src/components/app-sidebar/sidebar-user.tsx
+++ b/packages/ui/src/components/app-sidebar/sidebar-user.tsx
@@ -25,6 +25,7 @@ export const AppSidebarUser = ({ user, openThemeDialog, openLanguageDialog, hand
description={user?.email}
avatarFallback={userName}
src={user?.url}
+ className="!ml-0"
dropdownMenuContent={
<>
diff --git a/packages/ui/src/components/app-sidebar/types.ts b/packages/ui/src/components/app-sidebar/types.ts
index 4665d223a7..31fb4f2152 100644
--- a/packages/ui/src/components/app-sidebar/types.ts
+++ b/packages/ui/src/components/app-sidebar/types.ts
@@ -21,6 +21,7 @@ interface NavbarItemType {
description?: string
to: string
permanentlyPinned?: boolean
+ subItems?: NavbarItemType[]
}
export enum UserMenuKeys {
diff --git a/packages/ui/src/components/avatar.tsx b/packages/ui/src/components/avatar.tsx
index 8fc2e38395..ebcf80e926 100644
--- a/packages/ui/src/components/avatar.tsx
+++ b/packages/ui/src/components/avatar.tsx
@@ -34,24 +34,28 @@ export interface AvatarProps extends ComponentPropsWithoutRef<'span'> {
src?: string
size?: VariantProps['size']
rounded?: boolean
+ noInitials?: boolean
}
const Avatar = forwardRef(
- ({ name, src, size = 'sm', rounded = false, className, ...props }, ref) => {
- const initials = getInitials(name || '')
+ ({ name = '', src, size = 'sm', rounded = false, className, noInitials = false, ...props }, ref) => {
+ const initials = noInitials ? name : getInitials(name)
return (
{src ? (
<>
-
+
{/* TODO: Design system: Check whether we need cn-avatar-icon */}
{initials || }
>
) : (
-
+ 3 ? 'cn-avatar-fallback-small' : ''}`}
+ delayMs={0}
+ >
{initials || }
)}
diff --git a/packages/ui/src/components/button-group.tsx b/packages/ui/src/components/button-group.tsx
index 610f849aa0..3599a0d1c5 100644
--- a/packages/ui/src/components/button-group.tsx
+++ b/packages/ui/src/components/button-group.tsx
@@ -1,6 +1,6 @@
import { ComponentProps, FC, forwardRef, ReactNode, Ref } from 'react'
-import { Button, ButtonProps, DropdownMenu, toButtonProps, TooltipProps } from '@/components'
+import { Button, ButtonProps, DropdownMenu, TooltipProps } from '@/components'
import { cn } from '@utils/cn'
import omit from 'lodash-es/omit'
@@ -78,11 +78,9 @@ export const ButtonGroup = forwardRef(
)}
variant={variant ?? 'outline'}
size={size}
- {...toButtonProps({
- ...omit(restButtonProps, ['tooltipProps', 'dropdownProps']),
- iconOnly,
- tooltipProps: mergedTooltip
- })}
+ {...omit(restButtonProps, ['tooltipProps', 'dropdownProps'])}
+ iconOnly={iconOnly}
+ tooltipProps={mergedTooltip}
/>
)
diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx
index 8b1558d7c1..a7d1c0d9ca 100644
--- a/packages/ui/src/components/button.tsx
+++ b/packages/ui/src/components/button.tsx
@@ -1,10 +1,11 @@
-import { ButtonHTMLAttributes, forwardRef, Fragment } from 'react'
+import { ButtonHTMLAttributes, forwardRef, Fragment, useCallback, useState } from 'react'
import { Tooltip, TooltipProps } from '@components/tooltip'
import { Slot } from '@radix-ui/react-slot'
import { cn } from '@utils/cn'
-import { filterChildrenByDisplayNames } from '@utils/utils'
+import { filterChildrenByDisplayNames, isPromise } from '@utils/utils'
import { cva, type VariantProps } from 'class-variance-authority'
+import isEmpty from 'lodash-es/isEmpty'
import { IconV2, IconV2DisplayName } from './icon-v2'
@@ -30,10 +31,6 @@ const buttonVariants = cva('cn-button', {
true: 'cn-button-rounded'
},
- iconOnly: {
- true: 'cn-button-icon-only'
- },
-
theme: {
default: 'cn-button-default',
success: 'cn-button-success',
@@ -47,18 +44,18 @@ const buttonVariants = cva('cn-button', {
}
})
-type ButtonTooltipProps = Pick
+type ButtonTooltipProps = Pick
-type CommonButtonProps = ButtonHTMLAttributes &
+type CommonButtonProps = Omit, 'onClick'> &
VariantProps & {
asChild?: boolean
loading?: boolean
rounded?: boolean
+ onClick?: (event: React.MouseEvent) => void | Promise
}
type ButtonPropsIconOnlyRequired = {
iconOnly: true
- ignoreIconOnlyTooltip?: false
tooltipProps: ButtonTooltipProps
}
@@ -69,9 +66,8 @@ type ButtonPropsIconOnlyIgnored = {
}
type ButtonPropsRegular = {
- iconOnly?: false
+ iconOnly?: boolean
tooltipProps?: ButtonTooltipProps
- ignoreIconOnlyTooltip?: boolean
}
type ButtonProps = CommonButtonProps & (ButtonPropsIconOnlyRequired | ButtonPropsIconOnlyIgnored | ButtonPropsRegular)
@@ -96,19 +92,41 @@ const Button = forwardRef(
disabled,
children: _children,
type = 'button',
- tooltipProps,
- ignoreIconOnlyTooltip,
+ onClick,
...props
},
ref
) => {
+ const [isPromiseLoading, setIsPromiseLoading] = useState(false)
+
const Comp = asChild ? Slot : 'button'
const microSize = size === '2xs' || size === '3xs'
const iconOnly = iconOnlyProp || microSize
+ // Handle onClick that might return a promise
+ const handleClick = useCallback(
+ (event: React.MouseEvent) => {
+ if (onClick) {
+ const result = onClick(event)
+
+ // Check if onClick returned a promise (result could be void)
+ if (isPromise(result)) {
+ setIsPromiseLoading(true)
+ ;(result as Promise).finally(() => {
+ setIsPromiseLoading(false)
+ })
+ }
+ }
+ },
+ [onClick]
+ )
+
const filteredChildren = iconOnly ? filterChildrenByDisplayNames(_children, [IconV2DisplayName])[0] : _children
- const children = loading ? (
+ // Show loading if either loading prop is true or promise is loading
+ const isLoading = loading || isPromiseLoading
+
+ const children = isLoading ? (
<>
{/* When button state is 'loading' and iconOnly is true, we show only 1 icon */}
@@ -120,19 +138,24 @@ const Button = forwardRef(
const ButtonComp = (
{children}
)
- if (tooltipProps && !ignoreIconOnlyTooltip) {
+ if (!isEmpty(props.tooltipProps)) {
+ const buttonTooltipProps = props.tooltipProps
+
return (
-
+
{ButtonComp}
)
@@ -143,34 +166,7 @@ const Button = forwardRef(
)
Button.displayName = 'Button'
-/**
- * Converts iconOnly into a literal and returns props ready to be spread into
- * @param p
- */
-type BtnIconOnly = Extract
-type BtnRegular = Extract
-type ButtonLike = Omit, 'iconOnly' | 'ignoreIconOnlyTooltip'> & {
- iconOnly?: boolean
- ignoreIconOnlyTooltip?: boolean
-}
-
-const toButtonProps = (p: ButtonLike) => {
- if (p?.iconOnly) {
- return {
- ...p,
- iconOnly: true,
- ignoreIconOnlyTooltip: (p as any)?.ignoreIconOnlyTooltip ?? false
- } as BtnIconOnly
- }
-
- const { ignoreIconOnlyTooltip: _drop, ...rest } = p as any
- return {
- ...rest,
- iconOnly: false
- } as BtnRegular
-}
-
-export { Button, buttonVariants, toButtonProps }
+export { Button, buttonVariants }
export type {
ButtonProps,
ButtonThemes,
diff --git a/packages/ui/src/components/calendar.tsx b/packages/ui/src/components/calendar.tsx
index 3cd6a1472e..4e1a38b507 100644
--- a/packages/ui/src/components/calendar.tsx
+++ b/packages/ui/src/components/calendar.tsx
@@ -1,10 +1,10 @@
import * as React from 'react'
import { DayPicker, type DateRange } from 'react-day-picker'
-import { ChevronLeftIcon, ChevronRightIcon } from '@radix-ui/react-icons'
import { cn } from '@utils/cn'
import { buttonVariants } from './button'
+import { IconV2 } from './icon-v2'
export type CalendarProps = React.ComponentProps
export type CalendarDateRange = DateRange
@@ -50,8 +50,8 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
...classNames
}}
components={{
- IconLeft: () => ,
- IconRight: () =>
+ IconLeft: () => ,
+ IconRight: () =>
}}
{...props}
/>
diff --git a/packages/ui/src/components/carousel.tsx b/packages/ui/src/components/carousel.tsx
index 353558df1a..f67ed299aa 100644
--- a/packages/ui/src/components/carousel.tsx
+++ b/packages/ui/src/components/carousel.tsx
@@ -10,9 +10,8 @@ import {
useState
} from 'react'
-import { Button } from '@/components'
+import { Button, IconV2 } from '@/components'
import { cn } from '@/utils/cn'
-import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons'
import useEmblaCarousel, { type UseEmblaCarouselType } from 'embla-carousel-react'
type CarouselApi = UseEmblaCarouselType[1]
@@ -225,7 +224,7 @@ const CarouselPrevious = forwardRef(
}}
{...props}
>
-
+
Previous slide
)
@@ -258,7 +257,7 @@ const CarouselNext = forwardRef(
}}
{...props}
>
-
+
Next slide
)
diff --git a/packages/ui/src/components/command.tsx b/packages/ui/src/components/command.tsx
index 1e211d08bb..3930918a62 100644
--- a/packages/ui/src/components/command.tsx
+++ b/packages/ui/src/components/command.tsx
@@ -1,8 +1,7 @@
import * as React from 'react'
-import { Dialog, ScrollArea, ScrollAreaProps } from '@/components'
+import { Dialog, IconV2, ScrollArea, ScrollAreaProps } from '@/components'
import { type DialogProps } from '@radix-ui/react-dialog'
-import { MagnifyingGlassIcon } from '@radix-ui/react-icons'
import { cn } from '@utils/cn'
import { Command as CommandPrimitive } from 'cmdk'
@@ -36,7 +35,7 @@ const CommandInput = React.forwardRef<
>(({ className, ...props }, ref) => (
// eslint-disable-next-line react/no-unknown-property
-
+
{
data: TData[]
columns: ColumnDef[]
size?: VariantProps['size']
- pagination?: PaginationProps
+ paginationProps?: PaginationProps
getRowClassName?: (row: Row) => string | undefined
onRowClick?: (data: TData, index: number) => void
disableHighlightOnHover?: boolean
@@ -81,7 +81,7 @@ export function DataTable({
data = [],
columns,
size = 'normal',
- pagination,
+ paginationProps,
getRowClassName,
onRowClick,
disableHighlightOnHover = false,
@@ -230,61 +230,62 @@ export function DataTable({
const table = useReactTable(tableOptions)
return (
-
-
-
- {table.getHeaderGroups().map(headerGroup => (
-
- {headerGroup.headers.map(header => (
-
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
- {_enableColumnResizing && header.column.getCanResize() && (
-
- )}
-
+
+
+ {table.getHeaderGroups().map(headerGroup => (
+
+ {headerGroup.headers.map(header => (
+
+ {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
+ {_enableColumnResizing && header.column.getCanResize() && (
+
+ )}
+
+ ))}
+
+ ))}
+
+
+ {table.getRowModel().rows.map(row => (
+ <>
+ onRowClick(row.original, row.index) : undefined}
+ selected={enableRowSelection ? row.getIsSelected() : undefined}
+ >
+ {row.getVisibleCells().map(cell => (
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
))}
- ))}
-
-
- {table.getRowModel().rows.map(row => (
- <>
- onRowClick(row.original, row.index) : undefined}
- selected={enableRowSelection ? row.getIsSelected() : undefined}
- >
- {row.getVisibleCells().map(cell => (
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
- ))}
+ {row.getIsExpanded() && renderSubComponent && (
+
+
+ {renderSubComponent({ row })}
- {row.getIsExpanded() && renderSubComponent && (
-
-
- {renderSubComponent({ row })}
-
- )}
- >
- ))}
-
-
-
- {pagination &&
}
-
+ )}
+ >
+ ))}
+
+
)
}
diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx
index d37fb86581..5596026801 100644
--- a/packages/ui/src/components/dialog.tsx
+++ b/packages/ui/src/components/dialog.tsx
@@ -1,21 +1,7 @@
-import {
- Children,
- createContext,
- forwardRef,
- HTMLAttributes,
- isValidElement,
- MouseEvent,
- ReactNode,
- useCallback,
- useContext,
- useEffect,
- useRef,
- useState
-} from 'react'
+import { Children, forwardRef, HTMLAttributes, isValidElement, ReactNode, useCallback } from 'react'
-import { usePortal, useTranslation } from '@/context'
+import { DialogOpenContext, TriggerBase, usePortal, useRegisterDialog, useTranslation } from '@/context'
import * as DialogPrimitive from '@radix-ui/react-dialog'
-import { Slot } from '@radix-ui/react-slot'
import { cn } from '@utils/cn'
import { cva, type VariantProps } from 'class-variance-authority'
@@ -51,111 +37,8 @@ const headerVariants = cva('cn-modal-dialog-header', {
}
})
-interface FocusEntry {
- triggerId: string
- triggerElement: HTMLElement
-}
-
-interface DialogFocusContextValue {
- registerTrigger: (entry: FocusEntry) => void
- unregisterTrigger: (id: string) => void
- restoreFocus: (id: string) => void
- getLastTrigger: () => FocusEntry | undefined
-}
-
-const DialogContext = createContext(undefined)
-
-const useDialogFocusManager = () => {
- const context = useContext(DialogContext)
-
- return context
-}
-
-/**
- * DialogProvider component manages focus for dialog triggers.
- * It ensures that the last focused element is restored when the dialog is closed.
- */
-const DialogProvider = ({ children }: { children: ReactNode }) => {
- const focusStack = useRef([])
-
- const unregisterTrigger = useCallback((id: string) => {
- focusStack.current = focusStack.current.filter(e => e.triggerId !== id)
- }, [])
-
- const registerTrigger = useCallback((entry: FocusEntry) => {
- unregisterTrigger(entry.triggerId)
- focusStack.current.push(entry)
- }, [])
-
- const restoreFocus = useCallback((id: string) => {
- const entryIndex = focusStack.current.findIndex(e => e.triggerId === id)
-
- if (entryIndex !== -1) {
- const entry = focusStack.current[entryIndex]
- if (entry.triggerElement) {
- setTimeout(() => entry.triggerElement.focus(), 0)
- }
- unregisterTrigger(entry.triggerId)
- } else {
- const lastEntry = focusStack.current.at(-1)
- if (lastEntry?.triggerElement) {
- setTimeout(() => lastEntry.triggerElement.focus(), 0)
- }
- }
- }, [])
-
- const getLastTrigger = useCallback(() => {
- return focusStack.current.at(-1)
- }, [])
-
- return (
-
- {children}
-
- )
-}
-
-let triggerCounter = 0
-
-const useTriggerId = (_id?: string) => {
- const id = useRef(`${_id || 'dialog-trigger'}-${triggerCounter++}`)
- return id.current
-}
-
-/**
- * Custom hook to manage dialog trigger elements.
- * It provides a ref for the trigger element and a callback to register the trigger.
- * Useful when a dialog is opened from a complex components like dropdowns, etc.
- */
-const useCustomDialogTrigger = () => {
- const focusManager = useDialogFocusManager()
- const triggerId = useTriggerId()
- const triggerRef = useRef(null)
-
- const registerTrigger = useCallback(() => {
- if (focusManager && triggerRef.current) {
- focusManager.registerTrigger({ triggerId, triggerElement: triggerRef.current })
- }
- }, [focusManager, triggerId])
-
- return { triggerRef, registerTrigger }
-}
-
export type ModalDialogRootProps = Pick
-interface DialogOpenContextValue {
- open?: boolean
-}
-
-const DialogOpenContext = createContext(undefined)
-const useDialogOpen = () => {
- const context = useContext(DialogOpenContext)
- if (!context) {
- throw new Error('useDialogOpen must be used within a DialogOpenProvider')
- }
- return context
-}
-
const Root = ({ children, open, ...props }: ModalDialogRootProps) => {
return (
@@ -172,33 +55,17 @@ interface ContentProps extends DialogPrimitive.DialogContentProps, VariantProps<
}
const Content = forwardRef(
- ({ children, className, size = 'sm', hideClose = false, ...props }, ref) => {
+ ({ children, className, size = 'sm', hideClose = false, onCloseAutoFocus: _onCloseAutoFocus, ...props }, ref) => {
const { portalContainer } = usePortal()
- const focusManager = useDialogFocusManager()
- const [triggerId, setTriggerId] = useState('')
- const { open } = useDialogOpen()
-
- const handleCloseAutoFocus = useCallback(() => {
- if (focusManager) {
- focusManager.restoreFocus(triggerId)
- }
- }, [focusManager, triggerId])
-
- useEffect(() => {
- if (focusManager && open) {
- const triggerId = focusManager.getLastTrigger()?.triggerId
- if (!triggerId) return
- setTriggerId(triggerId)
- }
- }, [open])
-
- useEffect(() => {
- return () => {
- if (focusManager) {
- focusManager.unregisterTrigger(triggerId)
- }
- }
- }, [])
+ const { handleCloseAutoFocus } = useRegisterDialog()
+
+ const onCloseAutoFocus = useCallback(
+ (e: Event) => {
+ handleCloseAutoFocus()
+ _onCloseAutoFocus?.(e)
+ },
+ [_onCloseAutoFocus, handleCloseAutoFocus]
+ )
return (
@@ -209,8 +76,8 @@ const Content = forwardRef(
{!hideClose && (
@@ -328,47 +195,16 @@ const Close = forwardRef(({ children, className,
))
Close.displayName = 'Dialog.Close'
-/**
- * Dialog trigger component is essential for opening dialogs.
- * It registers the trigger element with the dialog focus manager.
- */
-const Trigger = forwardRef & { children: ReactNode }>(
- ({ onClick, id, children, ...props }, ref) => {
- const triggerId = useTriggerId(id)
- const focusManager = useDialogFocusManager()
-
- const dialogContext = useContext(DialogOpenContext)
- const isInsideDialog = dialogContext !== undefined
- const childrenCount = Children.count(children)
-
- if (childrenCount > 1) {
- console.warn('Dialog.Trigger: Only one child is allowed')
- children = {children}
- }
-
- const handleClick = (event: MouseEvent) => {
- if (focusManager) {
- focusManager.registerTrigger({ triggerId, triggerElement: event.currentTarget })
- }
- onClick?.(event)
- }
-
- const trigger = (
-
- {children}
-
- )
-
- if (isInsideDialog && !onClick) {
- return {trigger}
- }
-
- return trigger
- }
-)
+const Trigger = forwardRef>(({ children, ...props }, ref) => {
+ return (
+
+ {children}
+
+ )
+})
Trigger.displayName = 'DialogTrigger'
-const Dialog = {
+export const Dialog = {
Root,
Trigger,
Content,
@@ -379,5 +215,3 @@ const Dialog = {
Body,
Footer
}
-
-export { Dialog, DialogProvider, useCustomDialogTrigger }
diff --git a/packages/ui/src/components/drawer/DrawerContent.tsx b/packages/ui/src/components/drawer/DrawerContent.tsx
index 2091755306..fc628ff513 100644
--- a/packages/ui/src/components/drawer/DrawerContent.tsx
+++ b/packages/ui/src/components/drawer/DrawerContent.tsx
@@ -1,7 +1,7 @@
-import { ComponentPropsWithoutRef, ElementRef, forwardRef, useRef } from 'react'
+import { ComponentPropsWithoutRef, ElementRef, forwardRef, useCallback, useRef } from 'react'
import { Button, IconV2 } from '@/components'
-import { usePortal } from '@/context'
+import { usePortal, useRegisterDialog } from '@/context'
import { cn, useMergeRefs } from '@/utils'
import { cva, VariantProps } from 'class-variance-authority'
import { Drawer as DrawerPrimitive } from 'vaul'
@@ -12,6 +12,7 @@ import { DrawerOverlay } from './DrawerOverlay'
const drawerContentVariants = cva('cn-drawer-content', {
variants: {
size: {
+ '2xs': 'cn-drawer-content-2xs',
xs: 'cn-drawer-content-xs',
sm: 'cn-drawer-content-sm',
md: 'cn-drawer-content-md',
@@ -50,6 +51,7 @@ export const DrawerContent = forwardRef {
+ handleCloseAutoFocus()
+ _onCloseAutoFocus?.(e)
+ },
+ [_onCloseAutoFocus, handleCloseAutoFocus]
+ )
+
const withCustomOverlay = forceWithOverlay && modal === false
const Content = (
@@ -69,6 +81,7 @@ export const DrawerContent = forwardRef {
e.preventDefault()
onOpenAutoFocus?.(e)
diff --git a/packages/ui/src/components/drawer/DrawerRoot.tsx b/packages/ui/src/components/drawer/DrawerRoot.tsx
index 449f36f2fb..d3cff29bf8 100644
--- a/packages/ui/src/components/drawer/DrawerRoot.tsx
+++ b/packages/ui/src/components/drawer/DrawerRoot.tsx
@@ -1,6 +1,6 @@
import { ComponentProps, useEffect, useRef, useState } from 'react'
-import { usePortal } from '@/context'
+import { DialogOpenContext, usePortal } from '@/context'
import { Drawer as DrawerPrimitive } from 'vaul'
import styleText from 'vaul/style.css?raw'
@@ -71,10 +71,12 @@ export const DrawerRoot = ({
-
- {nested && FakeTriggers}
- {children}
-
+
+
+ {nested && FakeTriggers}
+ {children}
+
+
)
}
diff --git a/packages/ui/src/components/drawer/DrawerTrigger.tsx b/packages/ui/src/components/drawer/DrawerTrigger.tsx
new file mode 100644
index 0000000000..1c4be0dc83
--- /dev/null
+++ b/packages/ui/src/components/drawer/DrawerTrigger.tsx
@@ -0,0 +1,15 @@
+import { forwardRef, HTMLAttributes } from 'react'
+
+import { TriggerBase } from '@/context'
+import { Drawer as DrawerPrimitive } from 'vaul'
+
+export const DrawerTrigger = forwardRef>(
+ ({ children, ...props }, ref) => {
+ return (
+
+ {children}
+
+ )
+ }
+)
+DrawerTrigger.displayName = 'Dialog.Trigger'
diff --git a/packages/ui/src/components/drawer/index.ts b/packages/ui/src/components/drawer/index.ts
index 125987fe50..f58ed1d914 100644
--- a/packages/ui/src/components/drawer/index.ts
+++ b/packages/ui/src/components/drawer/index.ts
@@ -13,8 +13,8 @@ import { DrawerFooter } from './DrawerFooter'
import { DrawerHeader, DrawerHeaderProps } from './DrawerHeader'
import { DrawerRoot } from './DrawerRoot'
import { DrawerTitle } from './DrawerTitle'
+import { DrawerTrigger } from './DrawerTrigger'
-const DrawerTrigger = DrawerPrimitive.Trigger
const DrawerClose = DrawerPrimitive.Close
export type { DrawerContentVariantsSize, DrawerContentVariantsDirection, DrawerContentProps, DrawerHeaderProps }
diff --git a/packages/ui/src/components/dropdown-menu.tsx b/packages/ui/src/components/dropdown-menu.tsx
index c35e63109a..359115f2c2 100644
--- a/packages/ui/src/components/dropdown-menu.tsx
+++ b/packages/ui/src/components/dropdown-menu.tsx
@@ -459,11 +459,16 @@ DropdownMenuLogoItem.displayName = displayNames.logoItem
interface DropdownMenuIconItemProps extends Omit {
icon: IconPropsV2['name']
iconClassName?: string
+ iconSize?: IconPropsV2['size']
}
const DropdownMenuIconItem = forwardRef, DropdownMenuIconItemProps>(
- ({ icon, iconClassName, ...props }, ref) => (
- } />
+ ({ icon, iconClassName, iconSize, ...props }, ref) => (
+ }
+ />
)
)
DropdownMenuIconItem.displayName = displayNames.iconItem
diff --git a/packages/ui/src/components/favorite.tsx b/packages/ui/src/components/favorite.tsx
index befa373e57..320b8ba28b 100644
--- a/packages/ui/src/components/favorite.tsx
+++ b/packages/ui/src/components/favorite.tsx
@@ -1,29 +1,28 @@
import { FC } from 'react'
import { Toggle } from '@/components'
+import { cn } from '@/utils/cn'
interface FavoriteIconProps {
isFavorite?: boolean
onFavoriteToggle: (isFavorite: boolean) => void
+ className?: string
}
-const Favorite: FC = ({ isFavorite = false, onFavoriteToggle }) => (
+const Favorite: FC = ({ isFavorite = false, onFavoriteToggle, className }) => (
onFavoriteToggle(selected)}
tooltipProps={{
- content: isFavorite ? 'Remove from favorite' : 'Add to favorite'
+ content: isFavorite ? 'Unpin' : 'Pin'
}}
/>
)
-export { Favorite }
+export { Favorite, type FavoriteIconProps }
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 c3dd574f92..4cf613641f 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
@@ -10,9 +10,9 @@ import {
Tabs,
Tag,
Text,
- useCustomDialogTrigger,
ViewTypeValue
} from '@/components'
+import { useCustomDialogTrigger } from '@/context'
import { BranchSelectorTab } from '@views/repo/components/branch-selector-v2/types'
export interface FileViewerControlBarProps {
diff --git a/packages/ui/src/components/file-explorer.tsx b/packages/ui/src/components/file-explorer.tsx
index b47ce47305..c89c27b1c6 100644
--- a/packages/ui/src/components/file-explorer.tsx
+++ b/packages/ui/src/components/file-explorer.tsx
@@ -1,12 +1,32 @@
-import { ForwardedRef, forwardRef, ReactNode } from 'react'
-
-import { Accordion, GridProps, IconPropsV2, IconV2, Layout, Text, Tooltip, TooltipProps } from '@/components'
+import { ForwardedRef, forwardRef, ReactNode, useMemo } from 'react'
+
+import {
+ Accordion,
+ Button,
+ ButtonProps,
+ GridProps,
+ IconPropsV2,
+ IconV2,
+ IconV2NamesType,
+ Layout,
+ Text,
+ Tooltip,
+ TooltipProps
+} from '@/components'
import { LinkProps, useRouterContext } from '@/context'
import { cn } from '@utils/cn'
+type SidebarItemActionButtonPropsType = ButtonProps & {
+ title?: string
+ iconName?: IconV2NamesType
+ iconProps?: Omit
+ onClick: React.MouseEventHandler
+}
+
interface BaseItemProps {
icon: NonNullable
isActive?: boolean
+ actionButtons?: SidebarItemActionButtonPropsType[]
}
interface DefaultItemProps extends BaseItemProps, GridProps {
@@ -22,16 +42,33 @@ interface LinkItemProps extends BaseItemProps, Omit {
type ItemProps = DefaultItemProps | LinkItemProps
const Item = forwardRef(
- ({ className, children, icon, isActive, link, isFolder, ...props }: ItemProps, ref) => {
+ ({ className, children, icon, isActive, link, isFolder, actionButtons, ...props }: ItemProps, ref) => {
const { Link } = useRouterContext()
+ const actionButtonsContent = useMemo(() => {
+ if (!actionButtons) return null
+
+ return (
+
+ {actionButtons?.map((buttonProps, index) => {
+ const { title, iconOnly = true, iconName, iconProps, ...rest } = buttonProps
+ return (
+
+ {iconName && }
+ {title}
+
+ )
+ })}
+
+ )
+ }, [actionButtons])
+
const commonClassnames = cn(
- 'w-[fill-available] py-cn-2xs pr-1.5 rounded text-cn-2 hover:text-cn-1 focus-visible:text-cn-1 focus-visible:bg-cn-hover focus-visible:outline-none',
+ 'cn-file-tree-item',
{
- 'text-cn-1': isActive,
- 'grid items-center justify-start gap-cn-2xs grid-flow-col': !!link,
- 'bg-cn-selected': !isFolder && isActive,
- 'hover:bg-cn-hover': !isFolder
+ 'cn-file-tree-item-wrapper cn-file-tree-item-leaf': !isFolder,
+ 'cn-file-tree-item-active': !isFolder && isActive,
+ 'cn-file-tree-item-with-action-buttons': !!actionButtonsContent
},
className
)
@@ -43,26 +80,19 @@ const Item = forwardRef(
className={commonClassnames}
{...(props as Omit)}
>
-
-
+
+
{children}
+ {actionButtonsContent}
) : (
-
-
-
+
+
+
{children}
+ {actionButtonsContent}
)
}
@@ -72,34 +102,29 @@ Item.displayName = 'FileExplorerItem'
interface FolderItemProps {
children: ReactNode
level: number
- value?: string
+ value: string
isActive?: boolean
content?: ReactNode
+ onClick?: (value: string) => void
link?: string
}
-function FolderItem({ children, value = '', isActive, content, link, level }: FolderItemProps) {
+function FolderItem({ children, value = '', isActive, content, link, onClick, ...otherProps }: FolderItemProps) {
const itemElement = (
- -
+
- onClick?.(value)} {...otherProps}>
{children}
)
return (
-
+
@@ -109,9 +134,10 @@ function FolderItem({ children, value = '', isActive, content, link, level }: Fo
{!!content && (
{content}
+ {/* LOADER */}
)}
@@ -123,19 +149,19 @@ type FileItemProps = {
children: ReactNode
isActive?: boolean
link?: string
- onClick?: () => void
+ value: string
+ onClick?: (value: string) => void
tooltip?: TooltipProps['content']
[key: `data-${string}`]: any
}
-function FileItem({ children, isActive, level, link, onClick, tooltip, ...dataProps }: FileItemProps) {
+function FileItem({ children, isActive, value, link, onClick, tooltip, ...dataProps }: FileItemProps) {
const comp = (
- onClick?.(value)}
link={link}
{...dataProps}
>
@@ -166,4 +192,4 @@ function Root({ children, onValueChange, value }: RootProps) {
)
}
-export { Root, FileItem, FolderItem }
+export { Root, FileItem, FolderItem, Item }
diff --git a/packages/ui/src/components/filters/types.ts b/packages/ui/src/components/filters/types.ts
index af894061e4..07b3e6cf9e 100644
--- a/packages/ui/src/components/filters/types.ts
+++ b/packages/ui/src/components/filters/types.ts
@@ -19,6 +19,7 @@ export type SecretListFilters = {
secretTypes?: CheckboxOptions[]
secretManagerIdentifiers?: CheckboxOptions[]
description?: string
+ tags?: string
}
export interface CheckboxOptions {
diff --git a/packages/ui/src/components/form-input/index.ts b/packages/ui/src/components/form-input/index.ts
index 233aace76e..514693a64b 100644
--- a/packages/ui/src/components/form-input/index.ts
+++ b/packages/ui/src/components/form-input/index.ts
@@ -15,6 +15,19 @@ const FormInput = {
checked: field.value,
onCheckedChange: field.onChange
})),
+ /**
+ * Unlike other inputs, Select component's internal state is a generic type T,
+ * which is defined by the consumer of the component. Therefore, we need to
+ * explicitly define the type of the returned component here to ensure type safety.
+ *
+ * The `as` casting is necessary because `withForm` returns a generic component
+ * that doesn't inherently carry the specific type information of Select.
+ * By casting it, we inform TypeScript about the expected props and ref types.
+ *
+ * This ensures that when consumers use `FormInput.Select`, they get proper
+ * type checking and IntelliSense support for the Select component's props,
+ * including the generic type T.
+ */
Select: withForm(Select) as
(
props: FormSelectProps & { ref?: ForwardedRef }
) => ReactElement,
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 99abf6df4b..a8d56b6cbd 100644
--- a/packages/ui/src/components/icon-v2/icon-name-map.ts
+++ b/packages/ui/src/components/icon-v2/icon-name-map.ts
@@ -6,6 +6,7 @@
import AccountSolid from './icons/account-solid.svg'
import Account from './icons/account.svg'
import Agile from './icons/agile.svg'
+import AiDeepResearch from './icons/ai-deep-research.svg'
import AiMlOpsSolid from './icons/ai-ml-ops-solid.svg'
import AiMlOps from './icons/ai-ml-ops.svg'
import AiSolid from './icons/ai-solid.svg'
@@ -14,7 +15,6 @@ import AiTest from './icons/ai-test.svg'
import Ai from './icons/ai.svg'
import AppDiscoverySolid from './icons/app-discovery-solid.svg'
import AppDiscovery from './icons/app-discovery.svg'
-import AppNotification from './icons/app-notification.svg'
import AppSecurityTestsSolid from './icons/app-security-tests-solid.svg'
import AppSecurityTests from './icons/app-security-tests.svg'
import AppleShortcut from './icons/apple-shortcut.svg'
@@ -44,25 +44,11 @@ import ArrowUp from './icons/arrow-up.svg'
import ArrowsUpdown from './icons/arrows-updown.svg'
import ArtifactsSolid from './icons/artifacts-solid.svg'
import Artifacts from './icons/artifacts.svg'
-import AtSignCircle from './icons/at-sign-circle.svg'
import AtSign from './icons/at-sign.svg'
import AttachmentImage from './icons/attachment-image.svg'
import Attachment from './icons/attachment.svg'
-import BatteryCharging from './icons/battery-charging.svg'
-import BatteryEmpty from './icons/battery-empty.svg'
-import BatteryFull from './icons/battery-full.svg'
-import BatterySlash from './icons/battery-slash.svg'
-import BatteryWarning from './icons/battery-warning.svg'
-import Battery25 from './icons/battery25.svg'
-import Battery50 from './icons/battery50.svg'
-import Battery75 from './icons/battery75.svg'
-import BellNotification from './icons/bell-notification.svg'
import BellOff from './icons/bell-off.svg'
import Bell from './icons/bell.svg'
-import BinFull from './icons/bin-full.svg'
-import BinHalf from './icons/bin-half.svg'
-import BinMinusIn from './icons/bin-minus-in.svg'
-import BinPlusIn from './icons/bin-plus-in.svg'
import BoldSquere from './icons/bold-squere.svg'
import Bold from './icons/bold.svg'
import BookmarkSolid from './icons/bookmark-solid.svg'
@@ -99,6 +85,9 @@ import ChatBubble from './icons/chat-bubble.svg'
import ChatLines from './icons/chat-lines.svg'
import ChatMinusIn from './icons/chat-minus-in.svg'
import ChatPlusIn from './icons/chat-plus-in.svg'
+import ChatPositionFull from './icons/chat-position-full.svg'
+import ChatPositionLeft from './icons/chat-position-left.svg'
+import ChatPositionRight from './icons/chat-position-right.svg'
import CheckCircleSolid from './icons/check-circle-solid.svg'
import CheckCircle from './icons/check-circle.svg'
import Check from './icons/check.svg'
@@ -113,7 +102,6 @@ import CloudCostsSolid from './icons/cloud-costs-solid.svg'
import CloudCosts from './icons/cloud-costs.svg'
import CloudDesync from './icons/cloud-desync.svg'
import CloudDownload from './icons/cloud-download.svg'
-import CloudSquare from './icons/cloud-square.svg'
import CloudSync from './icons/cloud-sync.svg'
import CloudUpload from './icons/cloud-upload.svg'
import CloudXmark from './icons/cloud-xmark.svg'
@@ -128,7 +116,6 @@ import CollapseSidebar from './icons/collapse-sidebar.svg'
import Collapse from './icons/collapse.svg'
import Community from './icons/community.svg'
import Connectors from './icons/connectors.svg'
-import ControlSlider from './icons/control-slider.svg'
import Cookie from './icons/cookie.svg'
import Copy from './icons/copy.svg'
import CpuWarning from './icons/cpu-warning.svg'
@@ -137,7 +124,6 @@ import Crop from './icons/crop.svg'
import CursorPointer from './icons/cursor-pointer.svg'
import CustomSecretManager from './icons/custom-secret-manager.svg'
import CustomizeNavigation from './icons/customize-navigation.svg'
-import DashboardDots from './icons/dashboard-dots.svg'
import DashboardSolid from './icons/dashboard-solid.svg'
import DashboardSpeed from './icons/dashboard-speed.svg'
import Dashboard from './icons/dashboard.svg'
@@ -160,10 +146,9 @@ import Delegates from './icons/delegates.svg'
import DeploymentsSolid from './icons/deployments-solid.svg'
import Deployments from './icons/deployments.svg'
import Developer from './icons/developer.svg'
-import DocMagnifyingGlassIn from './icons/doc-magnifying-glass-in.svg'
import DocMagnifyingGlass from './icons/doc-magnifying-glass.svg'
-import DocStarIn from './icons/doc-star-in.svg'
import DocStar from './icons/doc-star.svg'
+import Docs from './icons/docs.svg'
import DoubleCheck from './icons/double-check.svg'
import DownloadCircleSolid from './icons/download-circle-solid.svg'
import DownloadCircle from './icons/download-circle.svg'
@@ -173,7 +158,6 @@ import DragHandGesture from './icons/drag-hand-gesture.svg'
import Drag from './icons/drag.svg'
import EditPencil from './icons/edit-pencil.svg'
import Edit from './icons/edit.svg'
-import Eject from './icons/eject.svg'
import EmptyPage from './icons/empty-page.svg'
import EnergyUsageWindow from './icons/energy-usage-window.svg'
import EngineeringInsightsSolid from './icons/engineering-insights-solid.svg'
@@ -186,10 +170,9 @@ import Executions from './icons/executions.svg'
import ExpandCode from './icons/expand-code.svg'
import ExpandSidebar from './icons/expand-sidebar.svg'
import Expand from './icons/expand.svg'
-import Externaltickets from './icons/externaltickets.svg'
+import ExternalTickets from './icons/external-tickets.svg'
import EyeClosed from './icons/eye-closed.svg'
import Eye from './icons/eye.svg'
-import Facetime from './icons/facetime.svg'
import FastArrowDown from './icons/fast-arrow-down.svg'
import FastArrowLeft from './icons/fast-arrow-left.svg'
import FastArrowRight from './icons/fast-arrow-right.svg'
@@ -202,12 +185,14 @@ import FilterListCircle from './icons/filter-list-circle.svg'
import FilterList from './icons/filter-list.svg'
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 FolderMinus from './icons/folder-minus.svg'
+import FolderOpenedSolid from './icons/folder-opened-solid.svg'
+import FolderOpened from './icons/folder-opened.svg'
import FolderPlus from './icons/folder-plus.svg'
import FolderSettings from './icons/folder-settings.svg'
+import FolderSolid from './icons/folder-solid.svg'
import FolderWarning from './icons/folder-warning.svg'
import Folder from './icons/folder.svg'
import ForwardMessage from './icons/forward-message.svg'
@@ -225,12 +210,6 @@ import GitRebase from './icons/git-rebase.svg'
import GitSquashMerge from './icons/git-squash-merge.svg'
import GithubAction from './icons/github-action.svg'
import Globe from './icons/globe.svg'
-import GoogleDocs from './icons/google-docs.svg'
-import GoogleDriveCheck from './icons/google-drive-check.svg'
-import GoogleDriveSync from './icons/google-drive-sync.svg'
-import GoogleDriveWarning from './icons/google-drive-warning.svg'
-import GoogleDrive from './icons/google-drive.svg'
-import GoogleOne from './icons/google-one.svg'
import GripDots from './icons/grip-dots.svg'
import Group1 from './icons/group-1.svg'
import HalfMoon from './icons/half-moon.svg'
@@ -249,12 +228,9 @@ import InfoCircleSolid from './icons/info-circle-solid.svg'
import InfoCircle from './icons/info-circle.svg'
import InfrastructureSolid from './icons/infrastructure-solid.svg'
import Infrastructure from './icons/infrastructure.svg'
-import InputSearch from './icons/input-search.svg'
import Internet from './icons/internet.svg'
import ItalicSquare from './icons/italic-square.svg'
import Italic from './icons/italic.svg'
-import JournalPage from './icons/journal-page.svg'
-import Journal from './icons/journal.svg'
import KanbanBoard from './icons/kanban-board.svg'
import KeyBack from './icons/key-back.svg'
import Key from './icons/key.svg'
@@ -262,7 +238,6 @@ import LabelSolid from './icons/label-solid.svg'
import Label from './icons/label.svg'
import LaptopDevMode from './icons/laptop-dev-mode.svg'
import LineSpace from './icons/line-space.svg'
-import Linux from './icons/linux.svg'
import ListSelect from './icons/list-select.svg'
import List from './icons/list.svg'
import Loader from './icons/loader.svg'
@@ -279,9 +254,6 @@ import Maximize from './icons/maximize.svg'
import MenuMoreHorizontal from './icons/menu-more-horizontal.svg'
import MenuScale from './icons/menu-scale.svg'
import Menu from './icons/menu.svg'
-import MessageAlert from './icons/message-alert.svg'
-import MessageText from './icons/message-text.svg'
-import Message from './icons/message.svg'
import MinusCircleSolid from './icons/minus-circle-solid.svg'
import MinusCircle from './icons/minus-circle.svg'
import MinusSquare from './icons/minus-square.svg'
@@ -295,36 +267,17 @@ import MultiplePagesEmpty from './icons/multiple-pages-empty.svg'
import MultiplePagesMinus from './icons/multiple-pages-minus.svg'
import MultiplePagesPlus from './icons/multiple-pages-plus.svg'
import MultiplePagesXmark from './icons/multiple-pages-xmark.svg'
-import MultiplePages from './icons/multiple-pages.svg'
import NavArrowDown from './icons/nav-arrow-down.svg'
import NavArrowLeft from './icons/nav-arrow-left.svg'
import NavArrowRight from './icons/nav-arrow-right.svg'
import NavArrowUp from './icons/nav-arrow-up.svg'
import NavSolidArrowDown from './icons/nav-solid-arrow-down.svg'
+import NewThread from './icons/new-thread.svg'
import Notes from './icons/notes.svg'
-import Number1SquareSolid from './icons/number-1-square-solid.svg'
-import Number1Square from './icons/number-1-square.svg'
-import Number2SquareSolid from './icons/number-2-square-solid.svg'
-import Number2Square from './icons/number-2-square.svg'
-import Number3SquareSolid from './icons/number-3-square-solid.svg'
-import Number3Square from './icons/number-3-square.svg'
-import Number4SquareSolid from './icons/number-4-square-solid.svg'
-import Number4Square from './icons/number-4-square.svg'
-import Number5SquareSolid from './icons/number-5-square-solid.svg'
-import Number5Square from './icons/number-5-square.svg'
-import Number6SquareSolid from './icons/number-6-square-solid.svg'
-import Number6Square from './icons/number-6-square.svg'
-import Number7SquareSolid from './icons/number-7-square-solid.svg'
-import Number7Square from './icons/number-7-square.svg'
-import Number8SquareSolid from './icons/number-8-square-solid.svg'
-import Number8Square from './icons/number-8-square.svg'
-import Number9SquareSolid from './icons/number-9-square-solid.svg'
-import Number9Square from './icons/number-9-square.svg'
import NumberedListLeft from './icons/numbered-list-left.svg'
import NumberedListRight from './icons/numbered-list-right.svg'
import Okrs from './icons/okrs.svg'
import OneFingerSelectHandGesture from './icons/one-finger-select-hand-gesture.svg'
-import OpenInBrowser from './icons/open-in-browser.svg'
import OpenInWindow from './icons/open-in-window.svg'
import OpenNewWindow from './icons/open-new-window.svg'
import OpenSelectHandGesture from './icons/open-select-hand-gesture.svg'
@@ -332,28 +285,18 @@ import OrganizationSolid from './icons/organization-solid.svg'
import Organizations from './icons/organizations.svg'
import Overrides from './icons/overrides.svg'
import PageDown from './icons/page-down.svg'
-import PageEdit from './icons/page-edit.svg'
import PageLeft from './icons/page-left.svg'
import PageMinusIn from './icons/page-minus-in.svg'
import PageMinus from './icons/page-minus.svg'
import PagePlusIn from './icons/page-plus-in.svg'
+import PagePlusMinusIn from './icons/page-plus-minus-in.svg'
import PagePlus from './icons/page-plus.svg'
import PageRight from './icons/page-right.svg'
-import PageSearch from './icons/page-search.svg'
-import PageStar from './icons/page-star.svg'
import PageUp from './icons/page-up.svg'
-import Page from './icons/page.svg'
import PasteClipboard from './icons/paste-clipboard.svg'
import PathArrow from './icons/path-arrow.svg'
import Pause from './icons/pause.svg'
import PeaceHand from './icons/peace-hand.svg'
-import PhoneDisabled from './icons/phone-disabled.svg'
-import PhoneIncome from './icons/phone-income.svg'
-import PhoneMinus from './icons/phone-minus.svg'
-import PhoneOutcome from './icons/phone-outcome.svg'
-import PhonePaused from './icons/phone-paused.svg'
-import PhonePlus from './icons/phone-plus.svg'
-import PhoneXmark from './icons/phone-xmark.svg'
import Phone from './icons/phone.svg'
import PhysicalDataCenter from './icons/physical-data-center.svg'
import PinSlashSolid from './icons/pin-slash-solid.svg'
@@ -368,16 +311,13 @@ import PlusCircleSolid from './icons/plus-circle-solid.svg'
import PlusCircle from './icons/plus-circle.svg'
import PlusSquare from './icons/plus-square.svg'
import Plus from './icons/plus.svg'
-import Podcast from './icons/podcast.svg'
import PortalSolid from './icons/portal-solid.svg'
import Portal from './icons/portal.svg'
import PrComment from './icons/pr-comment.svg'
import Presentation from './icons/presentation.svg'
-import PrivacyPolicy from './icons/privacy-policy.svg'
import Prohibition from './icons/prohibition.svg'
import Project from './icons/project.svg'
import QuestionMark from './icons/question-mark.svg'
-import QuoteMessage from './icons/quote-message.svg'
import Quote from './icons/quote.svg'
import RedoAction from './icons/redo-action.svg'
import RedoCircleSolid from './icons/redo-circle-solid.svg'
@@ -402,7 +342,7 @@ import RunTest from './icons/run-test.svg'
import Run from './icons/run.svg'
import RuntimeSecuritySolid from './icons/runtime-security-solid.svg'
import RuntimeSecurity from './icons/runtime-security.svg'
-import Safari from './icons/safari.svg'
+import Satellite from './icons/satellite.svg'
import ScissorAlt from './icons/scissor-alt.svg'
import Scissor from './icons/scissor.svg'
import SearchEngine from './icons/search-engine.svg'
@@ -412,7 +352,6 @@ import SecurityPass from './icons/security-pass.svg'
import SecurityTestsSolid from './icons/security-tests-solid.svg'
import SecurityTests from './icons/security-tests.svg'
import SelectWindow from './icons/select-window.svg'
-import SendDiagonal from './icons/send-diagonal.svg'
import SendMail from './icons/send-mail.svg'
import Send from './icons/send.svg'
import ServiceAccounts from './icons/service-accounts.svg'
@@ -427,9 +366,9 @@ import SingleView from './icons/single-view.svg'
import SlashSquare from './icons/slash-square.svg'
import Slash from './icons/slash.svg'
import Snowflake from './icons/snowflake.svg'
-import Sort2Down from './icons/sort-2-down.svg'
-import Sort2Up from './icons/sort-2-up.svg'
import Sort2 from './icons/sort-2.svg'
+import SortDown from './icons/sort-down.svg'
+import SortUp from './icons/sort-up.svg'
import SparksSolid from './icons/sparks-solid.svg'
import Sparks from './icons/sparks.svg'
import SplitView from './icons/split-view.svg'
@@ -486,6 +425,7 @@ import UserBadgeCheck from './icons/user-badge-check.svg'
import User from './icons/user.svg'
import Variables from './icons/variables.svg'
import Version from './icons/version.svg'
+import VideoCall from './icons/video-call.svg'
import ViewColumns2 from './icons/view-columns-2.svg'
import ViewColumns3 from './icons/view-columns-3.svg'
import ViewGrid from './icons/view-grid.svg'
@@ -499,7 +439,6 @@ import Webhook from './icons/webhook.svg'
import WorkspacesSolid from './icons/workspaces-solid.svg'
import Workspaces from './icons/workspaces.svg'
import WrapText from './icons/wrap-text.svg'
-import Www from './icons/www.svg'
import XmarkCircleSolid from './icons/xmark-circle-solid.svg'
import XmarkCircle from './icons/xmark-circle.svg'
import XmarkSquare from './icons/xmark-square.svg'
@@ -511,6 +450,7 @@ export const IconNameMapV2 = {
'account-solid': AccountSolid,
account: Account,
agile: Agile,
+ 'ai-deep-research': AiDeepResearch,
'ai-ml-ops-solid': AiMlOpsSolid,
'ai-ml-ops': AiMlOps,
'ai-solid': AiSolid,
@@ -519,7 +459,6 @@ export const IconNameMapV2 = {
ai: Ai,
'app-discovery-solid': AppDiscoverySolid,
'app-discovery': AppDiscovery,
- 'app-notification': AppNotification,
'app-security-tests-solid': AppSecurityTestsSolid,
'app-security-tests': AppSecurityTests,
'apple-shortcut': AppleShortcut,
@@ -549,25 +488,11 @@ export const IconNameMapV2 = {
'arrows-updown': ArrowsUpdown,
'artifacts-solid': ArtifactsSolid,
artifacts: Artifacts,
- 'at-sign-circle': AtSignCircle,
'at-sign': AtSign,
'attachment-image': AttachmentImage,
attachment: Attachment,
- 'battery-charging': BatteryCharging,
- 'battery-empty': BatteryEmpty,
- 'battery-full': BatteryFull,
- 'battery-slash': BatterySlash,
- 'battery-warning': BatteryWarning,
- battery25: Battery25,
- battery50: Battery50,
- battery75: Battery75,
- 'bell-notification': BellNotification,
'bell-off': BellOff,
bell: Bell,
- 'bin-full': BinFull,
- 'bin-half': BinHalf,
- 'bin-minus-in': BinMinusIn,
- 'bin-plus-in': BinPlusIn,
'bold-squere': BoldSquere,
bold: Bold,
'bookmark-solid': BookmarkSolid,
@@ -604,6 +529,9 @@ export const IconNameMapV2 = {
'chat-lines': ChatLines,
'chat-minus-in': ChatMinusIn,
'chat-plus-in': ChatPlusIn,
+ 'chat-position-full': ChatPositionFull,
+ 'chat-position-left': ChatPositionLeft,
+ 'chat-position-right': ChatPositionRight,
'check-circle-solid': CheckCircleSolid,
'check-circle': CheckCircle,
check: Check,
@@ -618,7 +546,6 @@ export const IconNameMapV2 = {
'cloud-costs': CloudCosts,
'cloud-desync': CloudDesync,
'cloud-download': CloudDownload,
- 'cloud-square': CloudSquare,
'cloud-sync': CloudSync,
'cloud-upload': CloudUpload,
'cloud-xmark': CloudXmark,
@@ -633,7 +560,6 @@ export const IconNameMapV2 = {
collapse: Collapse,
community: Community,
connectors: Connectors,
- 'control-slider': ControlSlider,
cookie: Cookie,
copy: Copy,
'cpu-warning': CpuWarning,
@@ -642,7 +568,6 @@ export const IconNameMapV2 = {
'cursor-pointer': CursorPointer,
'custom-secret-manager': CustomSecretManager,
'customize-navigation': CustomizeNavigation,
- 'dashboard-dots': DashboardDots,
'dashboard-solid': DashboardSolid,
'dashboard-speed': DashboardSpeed,
dashboard: Dashboard,
@@ -665,10 +590,9 @@ export const IconNameMapV2 = {
'deployments-solid': DeploymentsSolid,
deployments: Deployments,
developer: Developer,
- 'doc-magnifying-glass-in': DocMagnifyingGlassIn,
'doc-magnifying-glass': DocMagnifyingGlass,
- 'doc-star-in': DocStarIn,
'doc-star': DocStar,
+ docs: Docs,
'double-check': DoubleCheck,
'download-circle-solid': DownloadCircleSolid,
'download-circle': DownloadCircle,
@@ -678,7 +602,6 @@ export const IconNameMapV2 = {
drag: Drag,
'edit-pencil': EditPencil,
edit: Edit,
- eject: Eject,
'empty-page': EmptyPage,
'energy-usage-window': EnergyUsageWindow,
'engineering-insights-solid': EngineeringInsightsSolid,
@@ -691,10 +614,9 @@ export const IconNameMapV2 = {
'expand-code': ExpandCode,
'expand-sidebar': ExpandSidebar,
expand: Expand,
- externaltickets: Externaltickets,
+ 'external-tickets': ExternalTickets,
'eye-closed': EyeClosed,
eye: Eye,
- facetime: Facetime,
'fast-arrow-down': FastArrowDown,
'fast-arrow-left': FastArrowLeft,
'fast-arrow-right': FastArrowRight,
@@ -707,12 +629,14 @@ export const IconNameMapV2 = {
'filter-list': FilterList,
'filter-solid': FilterSolid,
filter: Filter,
- finder: Finder,
'fingerprint-window': FingerprintWindow,
'flag-solid': FlagSolid,
'folder-minus': FolderMinus,
+ 'folder-opened-solid': FolderOpenedSolid,
+ 'folder-opened': FolderOpened,
'folder-plus': FolderPlus,
'folder-settings': FolderSettings,
+ 'folder-solid': FolderSolid,
'folder-warning': FolderWarning,
folder: Folder,
'forward-message': ForwardMessage,
@@ -730,12 +654,6 @@ export const IconNameMapV2 = {
'git-squash-merge': GitSquashMerge,
'github-action': GithubAction,
globe: Globe,
- 'google-docs': GoogleDocs,
- 'google-drive-check': GoogleDriveCheck,
- 'google-drive-sync': GoogleDriveSync,
- 'google-drive-warning': GoogleDriveWarning,
- 'google-drive': GoogleDrive,
- 'google-one': GoogleOne,
'grip-dots': GripDots,
'group-1': Group1,
'half-moon': HalfMoon,
@@ -754,12 +672,9 @@ export const IconNameMapV2 = {
'info-circle': InfoCircle,
'infrastructure-solid': InfrastructureSolid,
infrastructure: Infrastructure,
- 'input-search': InputSearch,
internet: Internet,
'italic-square': ItalicSquare,
italic: Italic,
- 'journal-page': JournalPage,
- journal: Journal,
'kanban-board': KanbanBoard,
'key-back': KeyBack,
key: Key,
@@ -767,7 +682,6 @@ export const IconNameMapV2 = {
label: Label,
'laptop-dev-mode': LaptopDevMode,
'line-space': LineSpace,
- linux: Linux,
'list-select': ListSelect,
list: List,
loader: Loader,
@@ -784,9 +698,6 @@ export const IconNameMapV2 = {
'menu-more-horizontal': MenuMoreHorizontal,
'menu-scale': MenuScale,
menu: Menu,
- 'message-alert': MessageAlert,
- 'message-text': MessageText,
- message: Message,
'minus-circle-solid': MinusCircleSolid,
'minus-circle': MinusCircle,
'minus-square': MinusSquare,
@@ -800,36 +711,17 @@ export const IconNameMapV2 = {
'multiple-pages-minus': MultiplePagesMinus,
'multiple-pages-plus': MultiplePagesPlus,
'multiple-pages-xmark': MultiplePagesXmark,
- 'multiple-pages': MultiplePages,
'nav-arrow-down': NavArrowDown,
'nav-arrow-left': NavArrowLeft,
'nav-arrow-right': NavArrowRight,
'nav-arrow-up': NavArrowUp,
'nav-solid-arrow-down': NavSolidArrowDown,
+ 'new-thread': NewThread,
notes: Notes,
- 'number-1-square-solid': Number1SquareSolid,
- 'number-1-square': Number1Square,
- 'number-2-square-solid': Number2SquareSolid,
- 'number-2-square': Number2Square,
- 'number-3-square-solid': Number3SquareSolid,
- 'number-3-square': Number3Square,
- 'number-4-square-solid': Number4SquareSolid,
- 'number-4-square': Number4Square,
- 'number-5-square-solid': Number5SquareSolid,
- 'number-5-square': Number5Square,
- 'number-6-square-solid': Number6SquareSolid,
- 'number-6-square': Number6Square,
- 'number-7-square-solid': Number7SquareSolid,
- 'number-7-square': Number7Square,
- 'number-8-square-solid': Number8SquareSolid,
- 'number-8-square': Number8Square,
- 'number-9-square-solid': Number9SquareSolid,
- 'number-9-square': Number9Square,
'numbered-list-left': NumberedListLeft,
'numbered-list-right': NumberedListRight,
okrs: Okrs,
'one-finger-select-hand-gesture': OneFingerSelectHandGesture,
- 'open-in-browser': OpenInBrowser,
'open-in-window': OpenInWindow,
'open-new-window': OpenNewWindow,
'open-select-hand-gesture': OpenSelectHandGesture,
@@ -837,28 +729,18 @@ export const IconNameMapV2 = {
organizations: Organizations,
overrides: Overrides,
'page-down': PageDown,
- 'page-edit': PageEdit,
'page-left': PageLeft,
'page-minus-in': PageMinusIn,
'page-minus': PageMinus,
'page-plus-in': PagePlusIn,
+ 'page-plus-minus-in': PagePlusMinusIn,
'page-plus': PagePlus,
'page-right': PageRight,
- 'page-search': PageSearch,
- 'page-star': PageStar,
'page-up': PageUp,
- page: Page,
'paste-clipboard': PasteClipboard,
'path-arrow': PathArrow,
pause: Pause,
'peace-hand': PeaceHand,
- 'phone-disabled': PhoneDisabled,
- 'phone-income': PhoneIncome,
- 'phone-minus': PhoneMinus,
- 'phone-outcome': PhoneOutcome,
- 'phone-paused': PhonePaused,
- 'phone-plus': PhonePlus,
- 'phone-xmark': PhoneXmark,
phone: Phone,
'physical-data-center': PhysicalDataCenter,
'pin-slash-solid': PinSlashSolid,
@@ -873,16 +755,13 @@ export const IconNameMapV2 = {
'plus-circle': PlusCircle,
'plus-square': PlusSquare,
plus: Plus,
- podcast: Podcast,
'portal-solid': PortalSolid,
portal: Portal,
'pr-comment': PrComment,
presentation: Presentation,
- 'privacy-policy': PrivacyPolicy,
prohibition: Prohibition,
project: Project,
'question-mark': QuestionMark,
- 'quote-message': QuoteMessage,
quote: Quote,
'redo-action': RedoAction,
'redo-circle-solid': RedoCircleSolid,
@@ -907,7 +786,7 @@ export const IconNameMapV2 = {
run: Run,
'runtime-security-solid': RuntimeSecuritySolid,
'runtime-security': RuntimeSecurity,
- safari: Safari,
+ satellite: Satellite,
'scissor-alt': ScissorAlt,
scissor: Scissor,
'search-engine': SearchEngine,
@@ -917,7 +796,6 @@ export const IconNameMapV2 = {
'security-tests-solid': SecurityTestsSolid,
'security-tests': SecurityTests,
'select-window': SelectWindow,
- 'send-diagonal': SendDiagonal,
'send-mail': SendMail,
send: Send,
'service-accounts': ServiceAccounts,
@@ -932,9 +810,9 @@ export const IconNameMapV2 = {
'slash-square': SlashSquare,
slash: Slash,
snowflake: Snowflake,
- 'sort-2-down': Sort2Down,
- 'sort-2-up': Sort2Up,
'sort-2': Sort2,
+ 'sort-down': SortDown,
+ 'sort-up': SortUp,
'sparks-solid': SparksSolid,
sparks: Sparks,
'split-view': SplitView,
@@ -991,6 +869,7 @@ export const IconNameMapV2 = {
user: User,
variables: Variables,
version: Version,
+ 'video-call': VideoCall,
'view-columns-2': ViewColumns2,
'view-columns-3': ViewColumns3,
'view-grid': ViewGrid,
@@ -1004,7 +883,6 @@ export const IconNameMapV2 = {
'workspaces-solid': WorkspacesSolid,
workspaces: Workspaces,
'wrap-text': WrapText,
- www: Www,
'xmark-circle-solid': XmarkCircleSolid,
'xmark-circle': XmarkCircle,
'xmark-square': XmarkSquare,
diff --git a/packages/ui/src/components/icon-v2/icons/ai-deep-research.svg b/packages/ui/src/components/icon-v2/icons/ai-deep-research.svg
new file mode 100644
index 0000000000..d274adcab4
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/ai-deep-research.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/chat-position-full.svg b/packages/ui/src/components/icon-v2/icons/chat-position-full.svg
new file mode 100644
index 0000000000..d0fa68532c
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/chat-position-full.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/chat-position-left.svg b/packages/ui/src/components/icon-v2/icons/chat-position-left.svg
new file mode 100644
index 0000000000..d13a143895
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/chat-position-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/chat-position-right.svg b/packages/ui/src/components/icon-v2/icons/chat-position-right.svg
new file mode 100644
index 0000000000..7c46d64831
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/chat-position-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/doc-magnifying-glass.svg b/packages/ui/src/components/icon-v2/icons/doc-magnifying-glass.svg
index 5aa1bf952b..2a2bd30919 100644
--- a/packages/ui/src/components/icon-v2/icons/doc-magnifying-glass.svg
+++ b/packages/ui/src/components/icon-v2/icons/doc-magnifying-glass.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/doc-star.svg b/packages/ui/src/components/icon-v2/icons/doc-star.svg
index 64ec7a0e2b..759e4dabba 100644
--- a/packages/ui/src/components/icon-v2/icons/doc-star.svg
+++ b/packages/ui/src/components/icon-v2/icons/doc-star.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/docs.svg b/packages/ui/src/components/icon-v2/icons/docs.svg
new file mode 100644
index 0000000000..496218143a
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/docs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/external-tickets.svg b/packages/ui/src/components/icon-v2/icons/external-tickets.svg
new file mode 100644
index 0000000000..8ba95f0544
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/external-tickets.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 c4b9f92ce7..bfee2cdef3 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-opened-solid.svg b/packages/ui/src/components/icon-v2/icons/folder-opened-solid.svg
new file mode 100644
index 0000000000..8745b98dd0
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/folder-opened-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/folder-opened.svg b/packages/ui/src/components/icon-v2/icons/folder-opened.svg
new file mode 100644
index 0000000000..f71ef55cc5
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/folder-opened.svg
@@ -0,0 +1 @@
+
\ 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 82f0ed94c7..a666be7636 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-solid.svg b/packages/ui/src/components/icon-v2/icons/folder-solid.svg
new file mode 100644
index 0000000000..8b7f3bcaf9
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/folder-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/headset-help.svg b/packages/ui/src/components/icon-v2/icons/headset-help.svg
index 3c2b7d75a5..707ea6e277 100644
--- a/packages/ui/src/components/icon-v2/icons/headset-help.svg
+++ b/packages/ui/src/components/icon-v2/icons/headset-help.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/internet.svg b/packages/ui/src/components/icon-v2/icons/internet.svg
index f170769f94..68b9cf44b0 100644
--- a/packages/ui/src/components/icon-v2/icons/internet.svg
+++ b/packages/ui/src/components/icon-v2/icons/internet.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/mail-out.svg b/packages/ui/src/components/icon-v2/icons/mail-out.svg
index b6335bdd9c..d230e1d3e2 100644
--- a/packages/ui/src/components/icon-v2/icons/mail-out.svg
+++ b/packages/ui/src/components/icon-v2/icons/mail-out.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/new-thread.svg b/packages/ui/src/components/icon-v2/icons/new-thread.svg
new file mode 100644
index 0000000000..ed0b2d794a
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/new-thread.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/page-plus-minus-in.svg b/packages/ui/src/components/icon-v2/icons/page-plus-minus-in.svg
new file mode 100644
index 0000000000..17627eecd0
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/page-plus-minus-in.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/satellite.svg b/packages/ui/src/components/icon-v2/icons/satellite.svg
new file mode 100644
index 0000000000..c0e9f679e2
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/satellite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/send.svg b/packages/ui/src/components/icon-v2/icons/send.svg
index f9007f1e07..b316937b98 100644
--- a/packages/ui/src/components/icon-v2/icons/send.svg
+++ b/packages/ui/src/components/icon-v2/icons/send.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/sort-down.svg b/packages/ui/src/components/icon-v2/icons/sort-down.svg
new file mode 100644
index 0000000000..0bfb030fc4
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/sort-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/sort-up.svg b/packages/ui/src/components/icon-v2/icons/sort-up.svg
new file mode 100644
index 0000000000..26605dd38f
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/sort-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-v2/icons/video-call.svg b/packages/ui/src/components/icon-v2/icons/video-call.svg
new file mode 100644
index 0000000000..81f728601d
--- /dev/null
+++ b/packages/ui/src/components/icon-v2/icons/video-call.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/icon-with-tooltip.tsx b/packages/ui/src/components/icon-with-tooltip.tsx
index 07f4392ce9..d4c9d6c1a3 100644
--- a/packages/ui/src/components/icon-with-tooltip.tsx
+++ b/packages/ui/src/components/icon-with-tooltip.tsx
@@ -15,7 +15,7 @@ export interface IconWithTooltipProps extends Omit {
export const IconWithTooltip = ({ className, disabled, iconProps, ...props }: IconWithTooltipProps) => (
- )} />
+ )} />
)
diff --git a/packages/ui/src/components/illustration/images/no-data-folder-light.svg b/packages/ui/src/components/illustration/images/no-data-folder-light.svg
index cc8e60ec35..9c0db226d9 100644
--- a/packages/ui/src/components/illustration/images/no-data-folder-light.svg
+++ b/packages/ui/src/components/illustration/images/no-data-folder-light.svg
@@ -1,72 +1,50 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/ui/src/components/illustration/images/no-data-folder.svg b/packages/ui/src/components/illustration/images/no-data-folder.svg
index 2a5ebe4a1f..65c4ffe528 100644
--- a/packages/ui/src/components/illustration/images/no-data-folder.svg
+++ b/packages/ui/src/components/illustration/images/no-data-folder.svg
@@ -1,77 +1,50 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts
index f2c45fab26..3c12612816 100644
--- a/packages/ui/src/components/index.ts
+++ b/packages/ui/src/components/index.ts
@@ -7,6 +7,7 @@ export * from './drawer'
export * from './text'
export * from './spacer'
export * from './pagination/pagination'
+export type * from './pagination/types'
export * from './status-badge/status-badge'
export * from './counter-badge'
export * from './scroll-area'
@@ -20,7 +21,6 @@ export * from './no-data'
export * from './tabs'
export * from './command'
export * from './search-files'
-export * from './toast'
export * from './link'
export * from './commit-copy-actions'
export * from './accordion/accordion'
@@ -59,7 +59,6 @@ export * from './filters'
export * from './more-actions-tooltip'
export * from './split-button'
export * from './tooltip'
-export * from './navigation-menu'
export * from './pipeline-nodes'
export * from './sidebar'
export * from './separator'
@@ -101,3 +100,6 @@ export * from './form-input'
export * from './favorite'
export * from './scope'
export * from './rbac'
+
+// Sonner Toast
+export * from './toast'
diff --git a/packages/ui/src/components/input-otp.tsx b/packages/ui/src/components/input-otp.tsx
index d6f825b066..957ad55642 100644
--- a/packages/ui/src/components/input-otp.tsx
+++ b/packages/ui/src/components/input-otp.tsx
@@ -1,9 +1,10 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef, useContext } from 'react'
-import { DashIcon } from '@radix-ui/react-icons'
import { cn } from '@utils/cn'
import { OTPInput, OTPInputContext } from 'input-otp'
+import { IconV2 } from './icon-v2'
+
const InputOTPRoot = forwardRef, ComponentPropsWithoutRef>(
({ className, containerClassName, ...props }, ref) => (
(({ index, cla
{char}
{hasFakeCaret && (
)}
@@ -58,7 +59,7 @@ InputOTPSlot.displayName = 'InputOTPSlot'
const InputOTPSeparator = forwardRef, ComponentPropsWithoutRef<'div'>>(({ ...props }, ref) => (
-
+
))
InputOTPSeparator.displayName = 'InputOTPSeparator'
diff --git a/packages/ui/src/components/inputs/number-input.tsx b/packages/ui/src/components/inputs/number-input.tsx
index 6b67cf17e1..cfa948ba6a 100644
--- a/packages/ui/src/components/inputs/number-input.tsx
+++ b/packages/ui/src/components/inputs/number-input.tsx
@@ -137,10 +137,10 @@ export const NumberInput = forwardRef(
(!isStepperDisabled || !!suffix) && (
{!isStepperDisabled ? (
-
+
(
(
)
return (
-
+ <>
+
+ {/* NOTE: The hidden input below is required to prevent password managers from populating
+ the search field above. Previously this was happening when a user would use
+ their password manager to auto-populate something in a drawer form, e.g. entering
+ Secret Text. The password manager would find the search field above and assume
+ that was the matching username field. Traditionally this was prevented by using
+ autocomplete="off" but password managers have begun ignoring that property. Adding
+ a second hidden input confuses the password managers and prevents them from trying
+ to populate either of the two inputs with a username. Hacky, but this seems to be
+ the accepted solution for now.
+ */}
+
+ >
)
}
)
diff --git a/packages/ui/src/components/list-actions.tsx b/packages/ui/src/components/list-actions.tsx
index d0fce661a9..a791bdce24 100644
--- a/packages/ui/src/components/list-actions.tsx
+++ b/packages/ui/src/components/list-actions.tsx
@@ -1,10 +1,7 @@
import { ReactNode } from 'react'
-import { cn } from '@utils/cn'
-
-import { DropdownMenu } from './dropdown-menu'
-import { IconV2 } from './icon-v2'
-import { Text } from './text'
+import { Button, DropdownMenu, IconV2 } from '@/components'
+import { cn } from '@/utils'
interface DropdownItemProps {
name: string
@@ -34,16 +31,11 @@ function Right({ children, className }: { children: ReactNode; className?: strin
function Dropdown({ title, items, onChange, selectedValue }: DropdownProps) {
return (
-
- {selectedValue && }
-
+
+
{title}
-
-
+
+
{items && (
diff --git a/packages/ui/src/components/logo-v2/logo-name-map.ts b/packages/ui/src/components/logo-v2/logo-name-map.ts
index 7954f58db6..84e9ef6e32 100644
--- a/packages/ui/src/components/logo-v2/logo-name-map.ts
+++ b/packages/ui/src/components/logo-v2/logo-name-map.ts
@@ -48,6 +48,7 @@ import Npm from './logos/npm.svg'
import Oci from './logos/oci.svg'
import OpenShift from './logos/open-shift.svg'
import OpenTofu from './logos/open-tofu.svg'
+import Opsgenie from './logos/opsgenie.svg'
import Pagerduty from './logos/pagerduty.svg'
import Prometheus from './logos/prometheus.svg'
import Python from './logos/python.svg'
@@ -111,6 +112,7 @@ export const LogoNameMapV2 = {
oci: Oci,
'open-shift': OpenShift,
'open-tofu': OpenTofu,
+ opsgenie: Opsgenie,
pagerduty: Pagerduty,
prometheus: Prometheus,
python: Python,
diff --git a/packages/ui/src/components/logo-v2/logos/argo.svg b/packages/ui/src/components/logo-v2/logos/argo.svg
index e6af4b444e..b9317e0391 100644
--- a/packages/ui/src/components/logo-v2/logos/argo.svg
+++ b/packages/ui/src/components/logo-v2/logos/argo.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/grype.svg b/packages/ui/src/components/logo-v2/logos/grype.svg
index 3b46e12d7f..bfc45b0f56 100644
--- a/packages/ui/src/components/logo-v2/logos/grype.svg
+++ b/packages/ui/src/components/logo-v2/logos/grype.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/jenkins-x.svg b/packages/ui/src/components/logo-v2/logos/jenkins-x.svg
index 7b5829aa8d..f10e91ccab 100644
--- a/packages/ui/src/components/logo-v2/logos/jenkins-x.svg
+++ b/packages/ui/src/components/logo-v2/logos/jenkins-x.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/katalon.svg b/packages/ui/src/components/logo-v2/logos/katalon.svg
index 3e09592815..8fff442534 100644
--- a/packages/ui/src/components/logo-v2/logos/katalon.svg
+++ b/packages/ui/src/components/logo-v2/logos/katalon.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/linux.svg b/packages/ui/src/components/logo-v2/logos/linux.svg
index e017d2e7b8..1ada94b405 100644
--- a/packages/ui/src/components/logo-v2/logos/linux.svg
+++ b/packages/ui/src/components/logo-v2/logos/linux.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/new-relic.svg b/packages/ui/src/components/logo-v2/logos/new-relic.svg
index db52280fa1..f2278da341 100644
--- a/packages/ui/src/components/logo-v2/logos/new-relic.svg
+++ b/packages/ui/src/components/logo-v2/logos/new-relic.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/open-tofu.svg b/packages/ui/src/components/logo-v2/logos/open-tofu.svg
index 536dd62f7f..c6b6efaae0 100644
--- a/packages/ui/src/components/logo-v2/logos/open-tofu.svg
+++ b/packages/ui/src/components/logo-v2/logos/open-tofu.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/opsgenie.svg b/packages/ui/src/components/logo-v2/logos/opsgenie.svg
new file mode 100644
index 0000000000..bacc905611
--- /dev/null
+++ b/packages/ui/src/components/logo-v2/logos/opsgenie.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/servicenow.svg b/packages/ui/src/components/logo-v2/logos/servicenow.svg
index 96d2c11372..81cb79077b 100644
--- a/packages/ui/src/components/logo-v2/logos/servicenow.svg
+++ b/packages/ui/src/components/logo-v2/logos/servicenow.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/terragrunt.svg b/packages/ui/src/components/logo-v2/logos/terragrunt.svg
index 57c23c3bba..325934b7b4 100644
--- a/packages/ui/src/components/logo-v2/logos/terragrunt.svg
+++ b/packages/ui/src/components/logo-v2/logos/terragrunt.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/logo-v2/logos/vue-js.svg b/packages/ui/src/components/logo-v2/logos/vue-js.svg
index 3d94a25bf7..435d49c294 100644
--- a/packages/ui/src/components/logo-v2/logos/vue-js.svg
+++ b/packages/ui/src/components/logo-v2/logos/vue-js.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/ui/src/components/manage-navigation/index.tsx b/packages/ui/src/components/manage-navigation/index.tsx
index 2f7559b30c..932a6cb2fc 100644
--- a/packages/ui/src/components/manage-navigation/index.tsx
+++ b/packages/ui/src/components/manage-navigation/index.tsx
@@ -7,11 +7,11 @@ import { closestCenter, DndContext } from '@dnd-kit/core'
import { SortableContext } from '@dnd-kit/sortable'
import { DraggableItem } from './draggable-item'
-import { ManageNavigationSearch } from './manage-navigation-search'
interface ManageNavigationProps {
pinnedItems: NavbarItemType[]
- navbarMenuData: MenuGroupType[]
+ // Might be needed for search later
+ navbarMenuData?: MenuGroupType[]
showManageNavigation: boolean
recentItems: NavbarItemType[]
onSave: (recentItems: NavbarItemType[], currentPinnedItems: NavbarItemType[]) => void
@@ -24,19 +24,9 @@ const filterRecentItems = (pinnedItems: NavbarItemType[], recentItems: NavbarIte
return recentItems.filter(item => !pinnedItems.some(pinnedItem => pinnedItem.id === item.id))
}
-const filterMenuData = (menuData: MenuGroupType[], pinnedItems: NavbarItemType[]): MenuGroupType[] => {
- return menuData
- .map(group => ({
- ...group,
- items: group.items.filter(item => !pinnedItems.some(pinnedItem => pinnedItem.id === item.id))
- }))
- .filter(group => group.items.length > 0)
-}
-
export const ManageNavigation = ({
pinnedItems,
recentItems,
- navbarMenuData,
showManageNavigation,
onSave,
onClose,
@@ -121,16 +111,14 @@ export const ManageNavigation = ({
-
Pinned
{!currentPinnedItems.length ? (
- No pinned items
+
+ No pinned items
+
) : (
diff --git a/packages/ui/src/components/markdown-viewer/style.css b/packages/ui/src/components/markdown-viewer/style.css
index 2274bbe759..f2c64b58e3 100644
--- a/packages/ui/src/components/markdown-viewer/style.css
+++ b/packages/ui/src/components/markdown-viewer/style.css
@@ -251,7 +251,7 @@
@apply font-body-code bg-cn-1 border-cn-3 text-cn-1 mb-0 rounded-none border-0 p-0;
code {
- @apply bg-cn-1 !text-cn-1 rounded-none p-0;
+ @apply bg-cn-2 !text-cn-1 rounded-none p-0;
}
}
diff --git a/packages/ui/src/components/more-actions-tooltip.tsx b/packages/ui/src/components/more-actions-tooltip.tsx
index 5537d820ac..88f568c8ee 100644
--- a/packages/ui/src/components/more-actions-tooltip.tsx
+++ b/packages/ui/src/components/more-actions-tooltip.tsx
@@ -1,7 +1,7 @@
import { forwardRef } from 'react'
import { useRouterContext } from '@/context'
-import { Button, ButtonProps, ButtonVariants } from '@components/button'
+import { Button, ButtonProps, ButtonSizes, ButtonVariants } from '@components/button'
import { DropdownMenu } from '@components/dropdown-menu'
import { IconV2, type IconPropsV2 } from '@components/icon-v2'
import { Tooltip, TooltipProps } from '@components/tooltip'
@@ -28,6 +28,8 @@ export interface MoreActionsTooltipProps {
alignOffset?: number
className?: string
buttonVariant?: ButtonVariants
+ buttonSize?: ButtonSizes
+ disabled?: boolean
}
/**
@@ -35,7 +37,17 @@ export interface MoreActionsTooltipProps {
*/
export const MoreActionsTooltip = forwardRef(
(
- { theme, actions, iconName = 'more-vert', sideOffset = 2, alignOffset = 0, className, buttonVariant = 'ghost' },
+ {
+ theme,
+ actions,
+ iconName = 'more-vert',
+ sideOffset = 2,
+ alignOffset = 0,
+ className,
+ buttonVariant = 'ghost',
+ buttonSize = 'md',
+ disabled = false
+ },
ref
) => {
const { Link } = useRouterContext()
@@ -43,15 +55,17 @@ export const MoreActionsTooltip = forwardRef
-
+
diff --git a/packages/ui/src/components/multi-select.tsx b/packages/ui/src/components/multi-select.tsx
index 791076e761..45c7b749dc 100644
--- a/packages/ui/src/components/multi-select.tsx
+++ b/packages/ui/src/components/multi-select.tsx
@@ -24,6 +24,7 @@ import {
Tag
} from '@/components'
import { generateAlphaNumericHash } from '@/utils'
+import { csvToObject } from '@/utils/stringUtils'
import { useDebounceSearch } from '@hooks/use-debounce-search'
import { cn } from '@utils/cn'
import { cva, VariantProps } from 'class-variance-authority'
@@ -68,33 +69,6 @@ export interface MultiSelectOption {
onReset?: () => void
}
-/**
- * Creates a new MultiSelectOption from an input string
- * Handles both simple values and key:value format
- * @param inputValue The string value entered by the user
- * @returns A properly formatted MultiSelectOption
- */
-const createOptionFromInput = (inputValue: string): MultiSelectOption => {
- // Handle key:value format (e.g. "category:frontend")
- if (inputValue.includes(':')) {
- const [key, value] = inputValue.split(':', 2)
-
- if (key && key.trim()) {
- return {
- key: key.trim(),
- value: value ? value.trim() : '',
- id: inputValue
- }
- }
- }
-
- // Default case: use input as the key
- return {
- key: inputValue,
- id: inputValue
- }
-}
-
export interface MultiSelectProps extends CommonInputsProp {
value?: MultiSelectOption[]
defaultValue?: MultiSelectOption[]
@@ -220,24 +194,40 @@ export const MultiSelect = forwardRef(
}
}
if (e.key === 'Enter' && input.value && !disallowCreation) {
- // Perform case-insensitive comparison to prevent duplicate options with different casing
- // This ensures that 'React' and 'react' would be considered the same option
- if (
- !options?.some(option => option.key.toLowerCase() === input.value.toLowerCase()) &&
- (!availableOptions || availableOptions.length === 0) &&
- !getSelectedOptions().some(s => s.key.toLowerCase() === input.value.toLowerCase())
- ) {
- const newOption = createOptionFromInput(input.value)
- const newOptions = [...getSelectedOptions(), newOption]
- if (isControlled) {
- onChange?.(newOptions)
+ const inputValue = input.value.trim()
+ // Handle comma-separated input or single option
+ const { data: csvData, metadata: csvMetadata } = csvToObject(inputValue)
+ const updatedOptions = getSelectedOptions()
+
+ // Process each key-value pair from the CSV object
+ for (const [key, value] of Object.entries(csvData)) {
+ const wasKeyValuePair = csvMetadata[key] // Use metadata from csvToObject
+ const newOption = {
+ key,
+ value: wasKeyValuePair ? value : undefined, // Set value only for genuine key:value pairs
+ id: wasKeyValuePair ? `${key}:${value}` : key
+ }
+
+ const existingIndex = updatedOptions.findIndex(option => option.key === newOption.key)
+
+ if (existingIndex !== -1) {
+ // Replace existing option
+ updatedOptions[existingIndex] = newOption
} else {
- setSelected(newOptions)
+ // Add new option
+ updatedOptions.push(newOption)
}
- setInputValue('')
- setSearchQuery?.('')
- e.preventDefault()
}
+
+ // Update state and clear input
+ if (isControlled) {
+ onChange?.(updatedOptions)
+ } else {
+ setSelected(updatedOptions)
+ }
+ setInputValue('')
+ setSearchQuery?.('')
+ e.preventDefault()
}
if (e.key === 'Escape') {
input.blur()
diff --git a/packages/ui/src/components/navigation-menu.tsx b/packages/ui/src/components/navigation-menu.tsx
deleted file mode 100644
index 09b42385c5..0000000000
--- a/packages/ui/src/components/navigation-menu.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import * as React from 'react'
-
-import { ChevronDownIcon } from '@radix-ui/react-icons'
-import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'
-import { cn } from '@utils/cn'
-import { cva } from 'class-variance-authority'
-
-const NavigationMenuRoot = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
- {children}
-
-
-))
-NavigationMenuRoot.displayName = NavigationMenuPrimitive.Root.displayName
-
-const NavigationMenuList = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-))
-NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
-
-const NavigationMenuItem = NavigationMenuPrimitive.Item
-
-const navigationMenuTriggerStyle = cva(
- 'group inline-flex h-9 w-max items-center justify-center rounded-md bg-cn-1 px-4 py-2 text-sm font-medium transition-colors hover:bg-cn-3 hover:text-cn-1 focus:bg-cn-3 focus:text-cn-1 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-cn-3/50 data-[state=open]:bg-cn-3/50'
-)
-
-const NavigationMenuTrigger = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, children, ...props }, ref) => (
-
- {children}{' '}
-
-
-))
-NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
-
-const NavigationMenuContent = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-))
-NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
-
-const NavigationMenuLink = NavigationMenuPrimitive.Link
-
-const NavigationMenuViewport = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-
-
-))
-NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
-
-const NavigationMenuIndicator = React.forwardRef<
- React.ElementRef,
- React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
-
-
-
-))
-NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName
-
-const NavigationMenu = {
- Root: NavigationMenuRoot,
- List: NavigationMenuList,
- Item: NavigationMenuItem,
- Content: NavigationMenuContent,
- Trigger: NavigationMenuTrigger,
- Link: NavigationMenuLink,
- Indicator: NavigationMenuIndicator,
- Viewport: NavigationMenuViewport
-}
-
-export { NavigationMenu }
diff --git a/packages/ui/src/components/no-data.tsx b/packages/ui/src/components/no-data.tsx
index 8e36c61fba..d00037b27f 100644
--- a/packages/ui/src/components/no-data.tsx
+++ b/packages/ui/src/components/no-data.tsx
@@ -1,4 +1,4 @@
-import { Dispatch, FC, Fragment, ReactNode, SetStateAction } from 'react'
+import { Dispatch, FC, Fragment, ReactNode, Ref, SetStateAction } from 'react'
import {
Button,
@@ -10,8 +10,7 @@ import {
IllustrationsNameType,
Layout,
SplitButton,
- Text,
- toButtonProps
+ Text
} from '@/components'
import { useRouterContext } from '@/context'
import { cn } from '@utils/cn'
@@ -23,12 +22,14 @@ export interface NoDataProps {
imageSize?: number
description: string[]
primaryButton?: ButtonProps & {
+ ref?: Ref
icon?: IconPropsV2['name']
label: ReactNode | string
to?: string
isDialogTrigger?: boolean
}
secondaryButton?: ButtonProps & {
+ ref?: Ref
icon?: IconPropsV2['name']
label: ReactNode | string
to?: string
@@ -72,7 +73,7 @@ export const NoData: FC = ({
align="center"
justify="center"
className={cn(
- 'h-full w-full my-auto py-cn-4xl',
+ 'h-full w-full my-auto py-cn-4xl px-cn-2xl',
{ 'h-auto grow border border-cn-3 rounded-md': withBorder },
className
)}
@@ -92,7 +93,7 @@ export const NoData: FC = ({
{primaryButton &&
(primaryButton.to ? (
-
+
{primaryButton.icon && }
{primaryButton.label}
@@ -100,7 +101,7 @@ export const NoData: FC = ({
) : (
-
+
{primaryButton.icon && }
{primaryButton.label}
@@ -108,11 +109,7 @@ export const NoData: FC = ({
))}
{secondaryButton &&
(secondaryButton.to ? (
-
+
{secondaryButton.icon && }
{secondaryButton.label}
@@ -122,7 +119,7 @@ export const NoData: FC = ({
{secondaryButton.icon && }
{secondaryButton.label}
diff --git a/packages/ui/src/components/node-group.tsx b/packages/ui/src/components/node-group.tsx
index 69fad6b3a9..927091474a 100644
--- a/packages/ui/src/components/node-group.tsx
+++ b/packages/ui/src/components/node-group.tsx
@@ -13,7 +13,7 @@ function Root({ className, children }: NodeGroupRootProps) {
return (
@@ -25,14 +25,16 @@ function Root({ className, children }: NodeGroupRootProps) {
function Icon({
children,
simpleNodeIcon,
- className
+ className,
+ wrapperClassName
}: {
children?: ReactNode
simpleNodeIcon?: boolean
className?: string
+ wrapperClassName?: string
}) {
return (
-
+
(e: React.MouseEvent) => void
+ getPageLink?: (pageNum: number) => string
+ truncateLimit: number
+}
+export const PaginationItems: FC
= ({
+ totalPages,
+ currentPage,
+ goToPage,
+ getPageLink,
+ truncateLimit
+}) => {
+ // Calculate how many siblings to show around the current page
+ // The total visible pages would be: first + last + current + (siblings * 2) + 2 ellipses (at most)
+ // So we derive siblings from truncateLimit to ensure we don't exceed the limit
+ const siblings = Math.max(1, Math.floor((truncateLimit - 3) / 2))
+
+ // Special handling for pages near the beginning or end
+ let leftBound, rightBound
+
+ if (currentPage <= Math.ceil(truncateLimit / 2)) {
+ // Near the beginning - show first truncateLimit pages
+ leftBound = 2
+ rightBound = Math.min(totalPages - 1, truncateLimit)
+ } else if (currentPage > totalPages - Math.ceil(truncateLimit / 2)) {
+ // Near the end - show last truncateLimit pages
+ leftBound = Math.max(2, totalPages - truncateLimit + 1)
+ rightBound = totalPages - 1
+ } else {
+ // In the middle - show siblings on both sides
+ leftBound = Math.max(2, currentPage - siblings)
+ rightBound = Math.min(totalPages - 1, currentPage + siblings)
+ }
+ const items: React.ReactElement[] = []
+
+ // Always show the first page
+ items.push(
+
+
+ 1
+
+
+ )
+
+ // Add ellipsis if needed
+ if (leftBound > 2) {
+ items.push(
+
+
+
+ )
+ }
+
+ // Pages around the current page
+ for (let i = leftBound; i <= rightBound; i++) {
+ items.push(
+
+
+ {i}
+
+
+ )
+ }
+
+ // Add ellipsis if needed
+ if (rightBound < totalPages - 1) {
+ items.push(
+
+
+
+ )
+ }
+
+ // Always show the last page if it's different from the first page
+ if (totalPages > 1) {
+ items.push(
+
+
+ {totalPages}
+
+
+ )
+ }
+
+ return <>{items}>
+}
diff --git a/packages/ui/src/components/pagination/pagination-primitive.tsx b/packages/ui/src/components/pagination/pagination-primitive.tsx
index e54a199e22..dd0f60235e 100644
--- a/packages/ui/src/components/pagination/pagination-primitive.tsx
+++ b/packages/ui/src/components/pagination/pagination-primitive.tsx
@@ -1,6 +1,6 @@
import * as React from 'react'
-import { useRouterContext, useTranslation } from '@/context'
+import { useRouterContext } from '@/context'
import { IconV2 } from '@components/icon-v2'
import { cn } from '@utils/cn'
@@ -30,6 +30,7 @@ type PaginationPrimitiveLinkGeneralProps = {
interface PaginationPrimitiveLinkProps extends Omit {
disabled?: boolean
+ iconOnly?: boolean
}
const PaginationPrimitiveLink = ({
@@ -40,6 +41,7 @@ const PaginationPrimitiveLink = ({
ref,
disabled = false,
onClick,
+ iconOnly,
...props
}: PaginationPrimitiveLinkProps) => {
const { Link: LinkBase } = useRouterContext()
@@ -50,13 +52,13 @@ const PaginationPrimitiveLink = ({
if (onClick) {
return (
}
- variant="ghost"
+ variant="outline"
disabled={disabled}
onClick={onClick}
className={cn(
- 'cn-pagination-button',
{
'cn-button-active': isActive,
'cn-button-disabled': disabled
@@ -74,11 +76,11 @@ const PaginationPrimitiveLink = ({
)
}
+
PaginationPrimitiveLink.displayName = 'PaginationPrimitiveLink'
const PaginationPrimitivePrevious = ({
@@ -100,17 +103,16 @@ const PaginationPrimitivePrevious = ({
href = '#',
...props
}: PaginationPrimitiveLinkGeneralProps) => {
- const { t } = useTranslation()
return (
- {t('component:pagination.previous', 'Prev')}
)
}
@@ -122,16 +124,15 @@ const PaginationPrimitiveNext = ({
href = '#',
...props
}: PaginationPrimitiveLinkGeneralProps) => {
- const { t } = useTranslation()
return (
- {t('component:pagination.next', 'Next')}
)
diff --git a/packages/ui/src/components/pagination/pagination.tsx b/packages/ui/src/components/pagination/pagination.tsx
index 3fec40e264..e964bf8bf9 100644
--- a/packages/ui/src/components/pagination/pagination.tsx
+++ b/packages/ui/src/components/pagination/pagination.tsx
@@ -1,163 +1,29 @@
-import { FC, MouseEvent, ReactElement } from 'react'
-
+import { Select } from '@components/form-primitives'
+import { Layout } from '@components/layout'
+import { Separator } from '@components/separator'
+import { Text } from '@components/text'
import { cn } from '@utils/cn'
import { PaginationPrimitive } from './pagination-primitive'
+import { type PaginationProps } from './types'
-interface PaginationItemsProps {
- totalPages: number
- currentPage: number
- goToPage?: (pageNum: number) => (e: React.MouseEvent) => void
- getPageLink?: (pageNum: number) => string
- truncateLimit: number
-}
-
-const PaginationItems: FC = ({
- totalPages,
- currentPage,
- goToPage,
- getPageLink,
- truncateLimit
-}) => {
- // Calculate how many siblings to show around the current page
- // The total visible pages would be: first + last + current + (siblings * 2) + 2 ellipses (at most)
- // So we derive siblings from truncateLimit to ensure we don't exceed the limit
- const siblings = Math.max(1, Math.floor((truncateLimit - 3) / 2))
-
- // Special handling for pages near the beginning or end
- let leftBound, rightBound
-
- if (currentPage <= Math.ceil(truncateLimit / 2)) {
- // Near the beginning - show first truncateLimit pages
- leftBound = 2
- rightBound = Math.min(totalPages - 1, truncateLimit)
- } else if (currentPage > totalPages - Math.ceil(truncateLimit / 2)) {
- // Near the end - show last truncateLimit pages
- leftBound = Math.max(2, totalPages - truncateLimit + 1)
- rightBound = totalPages - 1
- } else {
- // In the middle - show siblings on both sides
- leftBound = Math.max(2, currentPage - siblings)
- rightBound = Math.min(totalPages - 1, currentPage + siblings)
- }
- const items: ReactElement[] = []
-
- // Always show the first page
- items.push(
-
-
- 1
-
-
- )
-
- // Add ellipsis if needed
- if (leftBound > 2) {
- items.push(
-
-
-
- )
- }
-
- // Pages around the current page
- for (let i = leftBound; i <= rightBound; i++) {
- items.push(
-
-
- {i}
-
-
- )
- }
-
- // Add ellipsis if needed
- if (rightBound < totalPages - 1) {
- items.push(
-
-
-
- )
- }
-
- // Always show the last page if it's different from the first page
- if (totalPages > 1) {
- items.push(
-
-
- {totalPages}
-
-
- )
- }
-
- return <>{items}>
-}
-
-interface PaginationBaseProps {
- className?: string
-}
-
-type DeterminatePaginationNavProps =
- | { goToPage: (page: number) => void; getPageLink?: never }
- | { goToPage?: never; getPageLink: (page: number) => string }
-
-type IndeterminatePaginationNavProps =
- | { onPrevious: () => void; onNext: () => void; getPrevPageLink?: never; getNextPageLink?: never }
- | { onPrevious?: never; onNext?: never; getPrevPageLink: () => string; getNextPageLink: () => string }
-
-type DeterminatePaginationProps = PaginationBaseProps &
- DeterminatePaginationNavProps & {
- totalItems: number
- pageSize: number
- currentPage: number
- hidePageNumbers?: boolean
- indeterminate?: false
-
- hasPrevious?: never
- hasNext?: never
- getPrevPageLink?: never
- getNextPageLink?: never
- onPrevious?: never
- onNext?: never
- }
-
-type IndeterminatePaginationProps = PaginationBaseProps &
- IndeterminatePaginationNavProps & {
- hasPrevious?: boolean
- hasNext?: boolean
- indeterminate: true
-
- goToPage?: never
- getPageLink?: never
- totalItems?: never
- pageSize?: never
- currentPage?: never
- hidePageNumbers?: never
- }
-
-export type PaginationProps = DeterminatePaginationProps | IndeterminatePaginationProps
-
-export const Pagination: FC = ({
+export function Pagination({
totalItems,
pageSize,
+ onPageSizeChange,
+ pageSizeOptions = [10, 25, 50],
currentPage,
goToPage,
getPageLink,
hasNext,
hasPrevious,
- className,
getPrevPageLink,
getNextPageLink,
onPrevious,
onNext,
- hidePageNumbers = false,
- indeterminate = false
-}) => {
+ indeterminate = false,
+ className
+}: PaginationProps) {
const handleGoToPage = (selectedPage?: number) => (e: React.MouseEvent) => {
e.preventDefault()
if (!selectedPage) return
@@ -167,72 +33,90 @@ export const Pagination: FC = ({
const totalPages = indeterminate || !totalItems || !pageSize ? undefined : Math.ceil(totalItems / pageSize)
- // Render nothing if `totalPages` is absent or <= 1, and both `nextPage` and `previousPage` are absent
- if ((!totalPages || totalPages <= 1) && hasNext === undefined && hasPrevious === undefined) {
+ // Render nothing if `totalPages` is absent, and both `nextPage` and `previousPage` are absent
+ if (!totalPages && hasNext === undefined && hasPrevious === undefined) {
return null
}
+ const handleChangePageSize = (value: number) => {
+ onPageSizeChange?.(value)
+ }
+
+ const renderItemsPerPageBlock = () => {
+ if (!onPageSizeChange) return null
+
+ const options = pageSizeOptions.map(option => ({ label: option, value: option }))
+
+ return (
+ <>
+
+
+ items per page
+
+
+
+ >
+ )
+ }
+
return (
- <>
-
- {!indeterminate && totalPages && currentPage ? (
-
- {/* Previous Button */}
-
- 1 ? currentPage - 1 : undefined) : undefined}
- href={getPageLink?.(currentPage > 1 ? currentPage - 1 : currentPage)}
- disabled={currentPage === 1}
- />
-
+
+
+ {totalPages && currentPage && (
+
+ {renderItemsPerPageBlock()}
+
+
+ Page {currentPage} of {totalPages}
+
+
+ )}
+
+
+ {!indeterminate && totalPages && currentPage ? (
+ <>
+ 1 ? currentPage - 1 : undefined) : undefined}
+ href={getPageLink?.(currentPage > 1 ? currentPage - 1 : currentPage)}
+ disabled={currentPage === 1}
+ />
+
+ {/* It is page number logic, and it can be used later if needed. */}
{/* Pagination Items */}
- {!hidePageNumbers && totalPages && (
-
- )}
-
- {/* Next Button */}
-
-
-
-
+ {/* {!showPageNumbers && totalPages && (
+
+ )} */}
+
+
+ >
) : (
-
- {/* Previous Button */}
-
-
-
- {/* Next Button */}
-
-
-
-
+ <>
+
+
+ >
)}
-
- >
+
+
)
}
-
-Pagination.displayName = 'Pagination'
diff --git a/packages/ui/src/components/pagination/types.ts b/packages/ui/src/components/pagination/types.ts
new file mode 100644
index 0000000000..51f5911613
--- /dev/null
+++ b/packages/ui/src/components/pagination/types.ts
@@ -0,0 +1,47 @@
+interface PaginationBaseProps {
+ className?: string
+}
+
+type DeterminatePaginationNavProps =
+ | { goToPage: (page: number) => void; getPageLink?: never }
+ | { goToPage?: never; getPageLink: (page: number) => string }
+
+type IndeterminatePaginationNavProps =
+ | { onPrevious: () => void; onNext: () => void; getPrevPageLink?: never; getNextPageLink?: never }
+ | { onPrevious?: never; onNext?: never; getPrevPageLink: () => string; getNextPageLink: () => string }
+
+type DeterminatePaginationProps = PaginationBaseProps &
+ DeterminatePaginationNavProps & {
+ totalItems: number
+ pageSize: number
+ pageSizeOptions?: number[]
+ onPageSizeChange?: (size: number) => void
+ currentPage: number
+ showPageNumbers?: boolean
+ indeterminate?: false
+
+ hasPrevious?: never
+ hasNext?: never
+ getPrevPageLink?: never
+ getNextPageLink?: never
+ onPrevious?: never
+ onNext?: never
+ }
+
+type IndeterminatePaginationProps = PaginationBaseProps &
+ IndeterminatePaginationNavProps & {
+ hasPrevious?: boolean
+ hasNext?: boolean
+ indeterminate: true
+
+ goToPage?: never
+ getPageLink?: never
+ totalItems?: never
+ pageSize?: never
+ pageSizeOptions?: never
+ onPageSizeChange?: never
+ currentPage?: never
+ showPageNumbers?: never
+ }
+
+export type PaginationProps = DeterminatePaginationProps | IndeterminatePaginationProps
diff --git a/packages/ui/src/components/pipeline-nodes/add-node.tsx b/packages/ui/src/components/pipeline-nodes/add-node.tsx
index 177e3f9790..d423c4413d 100644
--- a/packages/ui/src/components/pipeline-nodes/add-node.tsx
+++ b/packages/ui/src/components/pipeline-nodes/add-node.tsx
@@ -9,7 +9,7 @@ export function AddNode(props: AddNodeProp) {
const { onClick } = props
return (
-
+
>,
+ extends PropsWithChildren>,
Omit, 'children' | 'modal'> {
content: ReactNode
triggerType?: TriggerType
@@ -110,6 +110,7 @@ const PopoverComponent = ({
const [internalOpen, setInternalOpen] = useState(defaultOpen || false)
const hoverTimeoutRef = useRef(null)
const closeTimeoutRef = useRef(null)
+ const triggerRef = useRef(null)
useEffect(() => {
return () => {
@@ -162,10 +163,18 @@ const PopoverComponent = ({
return (
+
{children}
-
+ {
+ e.preventDefault()
+ triggerRef.current?.focus()
+ }}
+ {...props}
+ >
{content}
diff --git a/packages/ui/src/components/problems.tsx b/packages/ui/src/components/problems.tsx
index a6fd0deaf3..f7f19e1e47 100644
--- a/packages/ui/src/components/problems.tsx
+++ b/packages/ui/src/components/problems.tsx
@@ -31,7 +31,8 @@ export interface ProblemsProps {
const ProblemsComponent = {
Root: function Root({ children }: { children: React.ReactNode }) {
- return {children}
+ // TODO: needs design review
+ return {children}
},
Row: function Root({ onClick, children }: { onClick?: () => void; children: React.ReactNode }) {
@@ -40,7 +41,7 @@ const ProblemsComponent = {
role="button"
tabIndex={0}
onClick={onClick}
- className="width-100 flex flex-1 cursor-pointer items-center justify-between gap-2 text-nowrap px-4 py-0.5 text-cn-1"
+ className="width-100 text-cn-1 flex flex-1 cursor-pointer items-center justify-between gap-2 text-nowrap px-4 py-0.5"
>
{children}
@@ -68,7 +69,7 @@ const ProblemsComponent = {
}
}) {
return (
-
+
[{position.row}, {position.column}]
)
diff --git a/packages/ui/src/components/rbac/types.ts b/packages/ui/src/components/rbac/types.ts
index 86b2fa93fc..bff47fcaf7 100644
--- a/packages/ui/src/components/rbac/types.ts
+++ b/packages/ui/src/components/rbac/types.ts
@@ -1,3 +1,6 @@
+import { RefAttributes } from 'react'
+
+import { ActionData, MoreActionsTooltipProps } from '@/components'
import { ButtonProps } from '@components/button'
import { SplitButtonProps } from '@components/split-button'
import { TooltipProps } from '@components/tooltip'
@@ -11,12 +14,15 @@ export interface Resource {
}
export enum ResourceType {
- CODE_REPOSITORY = 'CODE_REPOSITORY'
+ CODE_REPOSITORY = 'CODE_REPOSITORY',
+ SECRET = 'SECRET'
}
export enum PermissionIdentifier {
CODE_REPO_CREATE = 'code_repo_create',
- CODE_REPO_DELETE = 'code_repo_delete'
+ CODE_REPO_DELETE = 'code_repo_delete',
+ UPDATE_SECRET = 'core_secret_edit',
+ DELETE_SECRET = 'core_secret_delete'
}
export interface PermissionsRequest {
@@ -24,10 +30,15 @@ export interface PermissionsRequest {
permissions: PermissionIdentifier[]
}
-interface RBACProps {
+export interface RBACProps {
rbac?: PermissionsRequest
}
+export interface RBACSplitProps extends RBACProps {
+ buttonRbac?: PermissionsRequest
+ dropdownRbac?: PermissionsRequest
+}
+
/**
* Types for RBAC-enabled components.
* These components will automatically handle RBAC checks based on the provided `rbac` prop.
@@ -37,4 +48,14 @@ export interface RbacButtonProps extends Omit
= SplitButtonProps &
- RBACProps & { tooltip?: Pick }
+ RBACSplitProps & { tooltip?: Pick }
+
+export interface RbacMoreActionsTooltipActionData extends ActionData, RBACProps {}
+
+export interface RbacMoreActionsTooltipProps
+ extends Omit,
+ RefAttributes {
+ actions: RbacMoreActionsTooltipActionData[]
+}
+
+export const rbacTooltip = 'You are missing the permission for this action.'
diff --git a/packages/ui/src/components/resizable.tsx b/packages/ui/src/components/resizable.tsx
index eb1db5cf62..50c3b1d519 100644
--- a/packages/ui/src/components/resizable.tsx
+++ b/packages/ui/src/components/resizable.tsx
@@ -1,9 +1,10 @@
import { ComponentProps } from 'react'
import * as ResizablePrimitive from 'react-resizable-panels'
-import { DragHandleDots2Icon } from '@radix-ui/react-icons'
import { cn } from '@utils/cn'
+import { IconV2 } from './icon-v2'
+
const ResizablePanelGroup = ({ className, ...props }: ComponentProps) => (
{withHandle && (
-
-
+
+
)}
diff --git a/packages/ui/src/components/sidebar/sidebar-item.tsx b/packages/ui/src/components/sidebar/sidebar-item.tsx
index 7b8a039ed0..641ee4b589 100644
--- a/packages/ui/src/components/sidebar/sidebar-item.tsx
+++ b/packages/ui/src/components/sidebar/sidebar-item.tsx
@@ -1,12 +1,14 @@
-import { ComponentPropsWithoutRef, forwardRef, ReactNode, Ref, useCallback, useEffect, useState } from 'react'
+import { ComponentPropsWithoutRef, forwardRef, ReactNode, Ref, useCallback, useEffect, useMemo, useState } from 'react'
import {
Avatar,
AvatarProps,
+ Button,
DropdownMenu,
DropdownMenuItemProps,
IconPropsV2,
IconV2,
+ IconV2NamesType,
Layout,
LogoPropsV2,
LogoV2,
@@ -14,7 +16,8 @@ import {
StatusBadge,
StatusBadgeProps,
Text,
- Tooltip
+ Tooltip,
+ type ButtonProps
} from '@/components'
import { NavLinkProps, useRouterContext } from '@/context'
import { filterChildrenByDisplayNames } from '@/utils'
@@ -29,11 +32,19 @@ interface SidebarBadgeProps extends Omit
+ onClick: React.MouseEventHandler
+}
+
interface SidebarItemCommonProps extends ComponentPropsWithoutRef<'button'> {
title: string
description?: string
tooltip?: ReactNode
active?: boolean
+ actionButtons?: SidebarItemActionButtonPropsType[]
}
interface SidebarItemWithChildrenProps extends SidebarItemCommonProps {
@@ -151,6 +162,7 @@ const SidebarItemTrigger = forwardRef 0
const withDropdownMenu = !!dropdownMenuContent
+ const withActionButtons = !!actionButtons
const withRightElement = withActionMenu || withDropdownMenu || !!badge || withSubmenu || withRightIndicator
const badgeCommonProps: Pick = {
@@ -174,12 +187,42 @@ const SidebarItemTrigger = forwardRef
+ const actionButtonsContent = useMemo(() => {
+ if (!withActionButtons) return null
+
+ return (
+
+ {actionButtons?.map((buttonProps, index) => {
+ const { title, iconOnly = true, iconName, iconProps, onClick: actionButtonOnClick, ...rest } = buttonProps
+ return (
+ {
+ e.preventDefault()
+ e.stopPropagation()
+ actionButtonOnClick(e)
+ }}
+ {...rest}
+ >
+ {iconName && }
+ {title}
+
+ )
+ })}
+
+ )
+ }, [actionButtons])
+
const renderContent = () => (
{(withIcon || withFallback) &&
@@ -205,7 +248,7 @@ const SidebarItemTrigger = forwardRef
)}
+ {/* Action buttons */}
+ {actionButtonsContent}
+
{withRightIndicator && }
{((withActionMenu && !badge) || withSubmenu) && (
diff --git a/packages/ui/src/components/sidebar/sidebar-units.tsx b/packages/ui/src/components/sidebar/sidebar-units.tsx
index 23086028ac..47b13e58ef 100644
--- a/packages/ui/src/components/sidebar/sidebar-units.tsx
+++ b/packages/ui/src/components/sidebar/sidebar-units.tsx
@@ -9,7 +9,17 @@ import {
useMemo
} from 'react'
-import { Button, IconV2, ScrollArea, Separator, Sheet, Text, useScrollArea } from '@/components'
+import {
+ Button,
+ IconV2,
+ IconV2NamesType,
+ Layout,
+ ScrollArea,
+ Separator,
+ Sheet,
+ Text,
+ useScrollArea
+} from '@/components'
import { useTranslation } from '@/context'
import { cn } from '@utils/cn'
@@ -154,15 +164,30 @@ export const SidebarContent = (props: ComponentProps) => {
export interface SidebarGroupProps extends ComponentPropsWithoutRef<'div'> {
label?: string
+ actionIcon?: IconV2NamesType
+ onActionClick?: () => void
}
export const SidebarGroup = forwardRef(
- ({ className, children, label, ...props }, ref) => (
+ ({ className, children, label, actionIcon = 'list', onActionClick, ...props }, ref) => (
{label && (
-
- {label}
-
+
+
+ {label}
+
+ {onActionClick && (
+
+
+
+ )}
+
)}
{children}
diff --git a/packages/ui/src/components/stacked-list.tsx b/packages/ui/src/components/stacked-list.tsx
index c931b44cfb..6cc475cba9 100644
--- a/packages/ui/src/components/stacked-list.tsx
+++ b/packages/ui/src/components/stacked-list.tsx
@@ -1,6 +1,6 @@
import { ComponentProps, FC, ReactNode } from 'react'
-import { Link, LinkProps, Text, TextProps } from '@/components'
+import { Link, LinkProps, Pagination, PaginationProps, Text, TextProps } from '@/components'
import { Slot, Slottable } from '@radix-ui/react-slot'
import { cn } from '@utils/cn'
import { cva, type VariantProps } from 'class-variance-authority'
@@ -16,17 +16,6 @@ export const stackedListVariants = cva('cn-stacked-list', {
}
})
-type ListProps = ComponentProps<'div'> & VariantProps
-
-const List: FC = ({ className, border, rounded, ...props }) => (
-
-)
-List.displayName = 'StackedList'
-
-/**
- * Item
- */
-
const spacings = ['4xs', '3xs', '2xs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl', '0'] as const
type Spacing = (typeof spacings)[number]
@@ -53,6 +42,42 @@ export const stackedListItemVariants = cva('cn-stacked-list-item', {
}
})
+type ListPaginationProps = PaginationProps & Pick, 'paddingX' | 'paddingY'>
+
+type ListProps = ComponentProps<'div'> &
+ VariantProps & {
+ paginationProps?: ListPaginationProps
+ }
+
+const List: FC = ({
+ children,
+ className,
+ border,
+ rounded,
+ paginationProps: {
+ className: paginationClassName,
+ paddingX,
+ paddingY,
+ ...paginationProps
+ } = {} as ListPaginationProps,
+ ...props
+}) => (
+
+ {children}
+ {paginationProps && (
+
+ )}
+
+)
+List.displayName = 'StackedList'
+
interface ListItemProps extends ComponentProps<'div'>, VariantProps {
thumbnail?: ReactNode
actions?: ReactNode
diff --git a/packages/ui/src/components/stats-panel.tsx b/packages/ui/src/components/stats-panel.tsx
index 57d09c7600..7c1284754a 100644
--- a/packages/ui/src/components/stats-panel.tsx
+++ b/packages/ui/src/components/stats-panel.tsx
@@ -1,24 +1,27 @@
-import { FC, ReactNode } from 'react'
+import { FC } from 'react'
-import { Layout, Text } from '@/components'
+import { Layout, Skeleton, Text } from '@/components'
export interface StatsPanelProps {
- data: { label: string; value: ReactNode }[]
+ data: { label: string; value?: JSX.Element }[]
+ isLoading?: boolean
}
-export const StatsPanel: FC = ({ data }) => {
+export const StatsPanel: FC = ({ data, isLoading = false }) => {
if (!data.length) return null
return (
-
+
{data.map((stat, index) => (
-
-
- {stat.label}
-
-
- {stat.value ? stat.value : '-'}
-
+
+ {stat.label}
+ {isLoading ? (
+
+ ) : (
+
+ {stat?.value ? stat.value : '-'}
+
+ )}
))}
diff --git a/packages/ui/src/components/table.tsx b/packages/ui/src/components/table.tsx
index ffdf85b430..a626aa6589 100644
--- a/packages/ui/src/components/table.tsx
+++ b/packages/ui/src/components/table.tsx
@@ -9,7 +9,18 @@ import {
ThHTMLAttributes
} from 'react'
-import { FlexProps, IconV2, Layout, Separator, Text, Tooltip, type LinkProps, type TooltipProps } from '@/components'
+import {
+ FlexProps,
+ IconV2,
+ Layout,
+ Pagination,
+ PaginationProps,
+ Separator,
+ Text,
+ Tooltip,
+ type LinkProps,
+ type TooltipProps
+} from '@/components'
import { useRouterContext } from '@/context'
import { cn } from '@/utils'
import { cva, type VariantProps } from 'class-variance-authority'
@@ -31,10 +42,21 @@ export const tableVariants = cva('cn-table-v2', {
export interface TableRootV2Props extends HTMLAttributes, VariantProps {
tableClassName?: string
disableHighlightOnHover?: boolean
+ paginationProps?: PaginationProps
}
const TableRoot = forwardRef(
- ({ size, className, tableClassName, disableHighlightOnHover = false, ...props }, ref) => (
+ (
+ {
+ size,
+ className,
+ tableClassName,
+ disableHighlightOnHover = false,
+ paginationProps: { className: paginationClassName, ...paginationProps } = {} as PaginationProps,
+ ...props
+ },
+ ref
+ ) => (
(
)}
>
+ {paginationProps && (
+
+ )}
)
)
diff --git a/packages/ui/src/components/time-ago-card.tsx b/packages/ui/src/components/time-ago-card.tsx
index f39e7efdb8..93787a9247 100644
--- a/packages/ui/src/components/time-ago-card.tsx
+++ b/packages/ui/src/components/time-ago-card.tsx
@@ -1,6 +1,6 @@
-import { FC, forwardRef, Fragment, memo, MouseEvent, Ref, useCallback, useState } from 'react'
+import { ButtonHTMLAttributes, FC, forwardRef, Fragment, memo, Ref } from 'react'
-import { Popover, PopoverTriggerProps, StatusBadge, Text, TextProps } from '@/components'
+import { StatusBadge, Text, TextProps, Tooltip, TooltipProps } from '@/components'
import { cn } from '@utils/cn'
import { LOCALE } from '@utils/TimeUtils'
import { formatDistanceToNow } from 'date-fns'
@@ -138,16 +138,14 @@ interface TimeAgoCardProps {
cutoffDays?: number
beforeCutoffPrefix?: string
afterCutoffPrefix?: string
- textProps?: TextProps<'time' | 'span'> & {
- ref?: Ref
- }
+ textProps?: Omit, 'ref'>
+ tooltipProps?: TooltipProps
+ triggerProps?: Omit, 'className'>
triggerClassName?: string
- contentClassName?: string
- triggerProps?: Omit
}
export const TimeAgoCard = memo(
- forwardRef(
+ forwardRef(
(
{
timestamp,
@@ -156,27 +154,18 @@ export const TimeAgoCard = memo(
beforeCutoffPrefix,
afterCutoffPrefix,
textProps,
- triggerClassName,
- contentClassName,
- triggerProps
+ tooltipProps,
+ triggerProps,
+ triggerClassName
},
ref
) => {
- const [isOpen, setIsOpen] = useState(false)
const { formattedShort, formattedFull, isBeyondCutoff } = useFormattedTime(
timestamp,
cutoffDays,
dateTimeFormatOptions
)
- const handleMouseEnter = useCallback(() => {
- setIsOpen(true)
- }, [])
-
- const handleMouseLeave = useCallback(() => {
- setIsOpen(false)
- }, [])
-
if (timestamp === null || timestamp === undefined) {
return (
@@ -185,30 +174,21 @@ export const TimeAgoCard = memo(
)
}
- const handleClickContent = (event: MouseEvent) => {
- event.stopPropagation()
- }
-
const hasPrefix = beforeCutoffPrefix || afterCutoffPrefix
const prefix = hasPrefix ? (isBeyondCutoff ? afterCutoffPrefix : beforeCutoffPrefix) : undefined
return (
-
-