diff --git a/apps/storybook/src/patterns/DapLayoutsPage.tsx b/apps/storybook/src/patterns/DapLayoutsPage.tsx index 013b23a43d..c2c900b98d 100644 --- a/apps/storybook/src/patterns/DapLayoutsPage.tsx +++ b/apps/storybook/src/patterns/DapLayoutsPage.tsx @@ -27,6 +27,7 @@ import { PromptEditor, RadioGroup, RadioGroupItem, + ReferenceChip, RequiredIndicator, Search, Select, @@ -1043,10 +1044,7 @@ function GmailConnectionPickerExample() { variant="ghost" > {selectedConnection ? ( - - - {selectedConnection} - + } /> ) : ( Select a Gmail connection )} diff --git a/packages/apollo-react/src/canvas/stories/templates/Flow.stories.tsx b/packages/apollo-react/src/canvas/stories/templates/Flow.stories.tsx index 897fe5075a..cac8c82ef8 100644 --- a/packages/apollo-react/src/canvas/stories/templates/Flow.stories.tsx +++ b/packages/apollo-react/src/canvas/stories/templates/Flow.stories.tsx @@ -33,6 +33,7 @@ import { type QuickFormFieldMode, RadioGroup, RadioGroupItem, + ReferenceChip, RequiredIndicator, ResizableHandle, ResizablePanel, @@ -84,6 +85,7 @@ import { Redo2, SlidersHorizontal, Sparkles, + SquareFunction, StickyNote, Trash2, Underline, @@ -1326,10 +1328,7 @@ function SendEmailForm({ spacious = false }: { spacious?: boolean }) {
- - - anurag.krishna@uipath.com - + } />
@@ -1363,9 +1362,10 @@ function SendEmailForm({ spacious = false }: { spacious?: boolean }) { ))}
- - ƒx $vars.recordUpdated1.output.Description - + } + />
diff --git a/packages/apollo-react/src/material/components/ap-chat/components/input/tiptap/resource-chip-node-view.tsx b/packages/apollo-react/src/material/components/ap-chat/components/input/tiptap/resource-chip-node-view.tsx index 9ca64bffdd..b71a400b48 100644 --- a/packages/apollo-react/src/material/components/ap-chat/components/input/tiptap/resource-chip-node-view.tsx +++ b/packages/apollo-react/src/material/components/ap-chat/components/input/tiptap/resource-chip-node-view.tsx @@ -5,17 +5,21 @@ import { type NodeViewProps, NodeViewWrapper } from '@tiptap/react'; import token, { Typography } from '@uipath/apollo-core'; import { useChatState } from '@uipath/apollo-react/chat/headless/providers/chat-state-provider'; import { CHAT_RESOURCE_CHIP_MAX_WIDTH } from '@uipath/apollo-react/chat/service'; +import { getReferenceChipColors } from '@uipath/apollo-wind/components/ui/reference-chip-colors'; import React from 'react'; import { fontByVariant } from '../../../utils/font-by-variant'; import { AutopilotChatActionButton } from '../../common/action-button'; import { AutopilotChatTooltip } from '../../common/tooltip'; +// Shared with apollo-wind's ReferenceChip so every reference chip uses the same token pair. +const chipColors = getReferenceChipColors().valid; + const ChipContent = styled('span')<{ readonly?: boolean }>(({ readonly }) => ({ display: 'inline-flex', alignItems: 'center', padding: `0 ${token.Padding.PadS}`, - backgroundColor: 'var(--color-info-background)', - color: 'var(--color-info-text)', + backgroundColor: chipColors.background, + color: chipColors.text, borderRadius: `calc(${token.Border.BorderRadiusL} * 2)`, verticalAlign: 'middle', ...(!readonly && { @@ -27,6 +31,7 @@ const ChipContent = styled('span')<{ readonly?: boolean }>(({ readonly }) => ({ const ChipIcon = styled('span')(() => ({ fontFamily: '"Material Icons Outlined"', + color: chipColors.icon, fontSize: token.Icon.IconXs, flexShrink: 0, userSelect: 'none', @@ -49,7 +54,7 @@ const ChipDeleteContainer = styled('span')<{ visible: boolean; compactMode: bool transform: 'translateY(-50%)', display: 'flex', alignItems: 'center', - backgroundColor: 'var(--color-info-background)', + backgroundColor: chipColors.background, borderRadius: `calc(${token.Border.BorderRadiusL} * 2)`, transition: 'opacity 0.15s ease-in-out', diff --git a/packages/apollo-wind/README.md b/packages/apollo-wind/README.md index 408e0e2b67..a404a11743 100644 --- a/packages/apollo-wind/README.md +++ b/packages/apollo-wind/README.md @@ -213,3 +213,28 @@ MIT - see [LICENSE](./LICENSE) **Peer Dependencies:** React ≥18.0.0 Built with ❤️ by UiPath | Inspired by [shadcn/ui](https://ui.shadcn.com) + +## Reference Chip + +`ReferenceChip` displays selected variables, resources and connections with paired +semantic background, text and icon colors. Find its API and state examples in +Storybook under **Apollo Wind → Components → Data Display → Reference Chip**. +The **Patterns → Code Editors → Editor Variables** example uses it through +`PromptEditor`; existing editor consumers need no API changes. + +```tsx +import { ReferenceChip } from '@uipath/apollo-wind'; + +} + onRemove={clearConnection} + removeLabel="Remove Outlook connection" +/> +``` + +The host supplies the icon, selection behavior and localized removal label. +`readOnly` hides removal, `disabled` disables removal, `invalid` uses the paired +error palette, and `selected` adds a visual outline. Keep removable chips outside +other buttons. Inside a picker trigger, omit `onRemove` and use a separate clear +button. Product connection fields must adopt this primitive to receive its styles. diff --git a/packages/apollo-wind/src/components/ui/code-editors.stories.tsx b/packages/apollo-wind/src/components/ui/code-editors.stories.tsx index 1d95172b43..ec109c1904 100644 --- a/packages/apollo-wind/src/components/ui/code-editors.stories.tsx +++ b/packages/apollo-wind/src/components/ui/code-editors.stories.tsx @@ -1205,7 +1205,7 @@ function EditorVariablesUsagePage() { ariaLabel="Prompt" />

- Try both: choose{' '} + PromptEditor uses the shared ReferenceChip for inserted variables. Try both: choose{' '} Insert → $vars → firstName above, or type $ in the editor and pick from the menu that appears. diff --git a/packages/apollo-wind/src/components/ui/index.ts b/packages/apollo-wind/src/components/ui/index.ts index 77ab25aa62..bb179a6529 100644 --- a/packages/apollo-wind/src/components/ui/index.ts +++ b/packages/apollo-wind/src/components/ui/index.ts @@ -54,6 +54,7 @@ export * from './message-scroller'; export * from './model-picker'; export * from './multi-select'; export * from './pagination'; +export * from './panel-tabs'; export * from './popover'; export * from './portal-container'; export * from './progress'; @@ -62,13 +63,15 @@ export * from './prompt-suggestions'; export * from './prompt-value-control'; export * from './quick-form-field'; export * from './radio-group'; +export { ReferenceChip, type ReferenceChipProps } from './reference-chip'; +export { getReferenceChipColors } from './reference-chip-colors'; export * from './resizable'; export * from './scroll-area'; export * from './search'; export * from './select'; export * from './separator'; -export * from './sidebar'; export * from './sheet'; +export * from './sidebar'; export * from './skeleton'; export * from './slider'; export * from './sonner'; @@ -78,7 +81,6 @@ export * from './stats-card'; export * from './stepper'; export * from './switch'; export * from './table'; -export * from './panel-tabs'; export * from './tabs'; export * from './textarea'; export * from './thinking-indicator'; diff --git a/packages/apollo-wind/src/components/ui/prompt-editor/components/MarkdownPreview.tsx b/packages/apollo-wind/src/components/ui/prompt-editor/components/MarkdownPreview.tsx index ea187234c2..a248ff9664 100644 --- a/packages/apollo-wind/src/components/ui/prompt-editor/components/MarkdownPreview.tsx +++ b/packages/apollo-wind/src/components/ui/prompt-editor/components/MarkdownPreview.tsx @@ -1,6 +1,7 @@ import DOMPurify from 'dompurify'; import { Marked } from 'marked'; import { useMemo } from 'react'; +import { getReferenceChipColors } from '../../reference-chip-colors'; import { usePromptEditorConfig } from '../prompt-editor-config'; import { type PromptEditorToken } from '../types'; import { buildTokenIconSvgMarkup } from './token-icon-markup'; @@ -13,6 +14,7 @@ const marked = new Marked({ async: false, gfm: true, breaks: true }); * do), and without a per-component JS `.css` import (which doesn't resolve in the bundleless build). * Scoped under `.prompt-editor-preview`; colors use design tokens. */ +const referenceColors = getReferenceChipColors().valid; const MARKDOWN_PREVIEW_STYLES = ` .prompt-editor-preview { color: var(--color-foreground); } .prompt-editor-preview-empty { color: var(--color-muted-foreground); } @@ -39,8 +41,8 @@ const MARKDOWN_PREVIEW_STYLES = ` .prompt-editor-preview th { font-weight: 600; background-color: var(--color-muted); } .prompt-editor-preview strong { font-weight: 700; } .prompt-editor-preview em { font-style: italic; } -.prompt-editor-preview .token-pill { display: inline-flex; align-items: center; gap: 3px; height: 20px; padding: 0 4px; border-radius: 4px; font-size: 13px; line-height: 20px; vertical-align: middle; background: var(--color-info-background); color: var(--color-info-text); } -.prompt-editor-preview .token-pill svg { display: block; flex-shrink: 0; color: var(--color-info-icon); width: 14px; height: 14px; } +.prompt-editor-preview .token-pill { display: inline-flex; align-items: center; gap: 3px; height: 20px; padding: 0 4px; border-radius: 4px; font-size: 13px; line-height: 20px; vertical-align: middle; background: ${referenceColors.background}; color: ${referenceColors.text}; } +.prompt-editor-preview .token-pill svg { display: block; flex-shrink: 0; color: ${referenceColors.icon}; width: 14px; height: 14px; } `; /** Host override for a variable pill's preview rendering (icon markup and/or label). */ diff --git a/packages/apollo-wind/src/components/ui/prompt-editor/components/TokenPill.tsx b/packages/apollo-wind/src/components/ui/prompt-editor/components/TokenPill.tsx index 2211ac4a25..86c3858539 100644 --- a/packages/apollo-wind/src/components/ui/prompt-editor/components/TokenPill.tsx +++ b/packages/apollo-wind/src/components/ui/prompt-editor/components/TokenPill.tsx @@ -1,8 +1,8 @@ import { Database, Paperclip, SquareFunction, Variable } from 'lucide-react'; import { memo } from 'react'; +import { ReferenceChip } from '../../reference-chip'; import { usePromptEditorConfig } from '../prompt-editor-config'; import type { PromptEditorDiffType, PromptEditorTokenType } from '../types'; -import { getPromptEditorTokenColors } from '../types'; /** Leading icon per token type — mirrors the SVG markup `MarkdownPreview` inlines for preview mode. */ const TOKEN_TYPE_ICON: Record< @@ -41,83 +41,27 @@ const TokenPillBase = ({ onMouseDown, }: TokenPillProps) => { const { strings } = usePromptEditorConfig(); - const PROMPT_EDITOR_TOKEN_COLORS = getPromptEditorTokenColors(); - const colors = - isInvalid && !diffType ? PROMPT_EDITOR_TOKEN_COLORS.invalid : PROMPT_EDITOR_TOKEN_COLORS.valid; const Icon = TOKEN_TYPE_ICON[tokenType]; - - const handleRemoveMouseDown = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - }; - - const handleRemoveClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onRemove?.(); - }; - - const bgColor = diffType - ? diffType === 'added' - ? 'rgba(76, 175, 80, 0.15)' - : 'rgba(244, 67, 54, 0.15)' - : colors.background; - - const outerClassName = [ - 'relative inline-flex items-center align-middle gap-[3px] h-5 rounded px-1 text-[13px] leading-5 outline-offset-1', - readonly ? 'cursor-default' : 'cursor-pointer', - diffType === 'removed' ? 'line-through opacity-60' : '', - isSelected ? 'z-10' : '', - ] - .filter(Boolean) - .join(' '); - return ( - // biome-ignore lint/a11y/noStaticElementInteractions: the pill is a Lexical decorator chip; mousedown sets node selection. Keyboard selection is handled by Lexical at the editor root, and the chip exposes a focusable Remove button. - } + onRemove={onRemove} + removeLabel={strings.removeToken} + readOnly={readonly} + invalid={isInvalid && !diffType} + selected={isSelected} onMouseDown={onMouseDown} - className={outerClassName} - style={{ - backgroundColor: bgColor, - outline: isSelected ? `2px solid ${colors.border}` : 'none', - }} - > - - - - - {value} - - {!readonly && ( - - )} - + className={`${readonly ? 'cursor-default' : 'cursor-pointer'} ${diffType === 'removed' ? 'line-through opacity-60' : ''}`} + style={ + diffType + ? { + backgroundColor: + diffType === 'added' ? 'rgba(76, 175, 80, 0.15)' : 'rgba(244, 67, 54, 0.15)', + } + : undefined + } + /> ); }; diff --git a/packages/apollo-wind/src/components/ui/prompt-editor/types.ts b/packages/apollo-wind/src/components/ui/prompt-editor/types.ts index 2815c9e8a4..2fcf39d15b 100644 --- a/packages/apollo-wind/src/components/ui/prompt-editor/types.ts +++ b/packages/apollo-wind/src/components/ui/prompt-editor/types.ts @@ -1,3 +1,5 @@ +import { getReferenceChipColors } from '../reference-chip-colors'; + export type PromptEditorTokenType = 'input' | 'output' | 'state' | 'resource' | 'text'; interface BaseToken { @@ -89,23 +91,7 @@ export interface PromptEditorTokenColorConfig { * Two-state palette: invalid = red, valid = blue. Backed by apollo-wind's semantic design tokens, * which already resolve per light/dark theme via CSS — no JS theme detection needed. */ -export const getPromptEditorTokenColors = (): Record< - 'valid' | 'invalid', - PromptEditorTokenColorConfig -> => ({ - valid: { - background: 'var(--color-info-background)', - border: 'var(--color-info-icon)', - text: 'var(--color-info-text)', - icon: 'var(--color-info-icon)', - }, - invalid: { - background: 'var(--color-error-background)', - border: 'var(--color-error-icon)', - text: 'var(--color-error-text)', - icon: 'var(--color-error-icon)', - }, -}); +export const getPromptEditorTokenColors = getReferenceChipColors; /** The token-type label subset of `PromptEditorStrings` (structural, to avoid a config↔types cycle). */ export interface PromptEditorTokenTypeLabels { diff --git a/packages/apollo-wind/src/components/ui/reference-chip-colors.ts b/packages/apollo-wind/src/components/ui/reference-chip-colors.ts new file mode 100644 index 0000000000..27b29ea773 --- /dev/null +++ b/packages/apollo-wind/src/components/ui/reference-chip-colors.ts @@ -0,0 +1,15 @@ +/** Paired reference colors. Keep labels, icons and backgrounds on the same semantic palette. */ +export const getReferenceChipColors = () => ({ + valid: { + background: 'var(--color-info-background)', + border: 'var(--color-info-icon)', + text: 'var(--color-info-text)', + icon: 'var(--color-info-icon)', + }, + invalid: { + background: 'var(--color-error-background)', + border: 'var(--color-error-icon)', + text: 'var(--color-error-text)', + icon: 'var(--color-error-icon)', + }, +}); diff --git a/packages/apollo-wind/src/components/ui/reference-chip.stories.tsx b/packages/apollo-wind/src/components/ui/reference-chip.stories.tsx new file mode 100644 index 0000000000..3717b7f1a9 --- /dev/null +++ b/packages/apollo-wind/src/components/ui/reference-chip.stories.tsx @@ -0,0 +1,219 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { Check, Database, Mail, Variable, X } from 'lucide-react'; +import { type ReactNode, useState } from 'react'; +import { Button } from './button'; +import { ReferenceChip } from './reference-chip'; +import { getReferenceChipColors } from './reference-chip-colors'; + +const meta = { + title: 'Components/Data Display/Reference Chip', + component: ReferenceChip, + tags: ['autodocs'], + decorators: [ + (Story) => ( +

+ +
+ ), + ], + args: { label: 'vars.firstName', icon: }, + parameters: { + docs: { + description: { + component: `A compact reference to a variable, resource or connection. PromptEditor uses this primitive through TokenPill. The connection picker owns its popup and selection behavior; ReferenceChip owns the selected value's appearance. + +Colors come from existing semantic tokens, always as a pair from one family. Valid references use --color-info-background with --color-info-text and --color-info-icon. Invalid references use --color-error-background with --color-error-text and --color-error-icon. The selected outline uses the icon token of the same family. getReferenceChipColors() returns these values for hosts that cannot render the component. Do not fill a chip with a primary or accent color and pick a foreground separately: that is how low-contrast chips happen. Branded icons may retain their colors. The Themes story shows every theme side by side. + +Pass onRemove to expose a keyboard-accessible remove button and removeLabel for localization. readOnly hides removal; disabled disables it. selected is visual only: the host owns selection semantics. Keep a removable chip outside other buttons. Inside a picker trigger, omit onRemove and provide a separate clear button. + +Import { ReferenceChip } from '@uipath/apollo-wind'. Existing PromptEditor consumers inherit this treatment without changing their API. Product connection fields must adopt the component; this does not update external product repositories.`, + }, + }, + }, +} satisfies Meta; +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const States: Story = { + render: () => ( +
+ } onRemove={() => {}} /> + } selected onRemove={() => {}} /> + } + invalid + onRemove={() => {}} + /> + } disabled onRemove={() => {}} /> + } readOnly onRemove={() => {}} /> + } + onRemove={() => {}} + /> +
+ ), +}; + +function ConnectionExample() { + const [connection, setConnection] = useState('alex@example.com'); + return ( +
+

+ Microsoft Outlook 365 connection +

+
+ {connection ? ( + } + removeLabel="Remove Outlook connection" + onRemove={() => setConnection('')} + /> + ) : ( + No connection selected + )} +
+ {!connection && ( + + )} +

+ The same chip used by prompt variables, with a connection icon and accessible removal. +

+
+ ); +} +export const Connection: Story = { render: () => }; + +function GuidanceCard({ + kind, + note, + children, +}: { + kind: 'do' | 'dont'; + note: string; + children: ReactNode; +}) { + return ( +
+
+ {kind === 'do' ? ( +
+
{children}
+

{note}

+
+ ); +} + +function PickerTrigger({ children }: { children: ReactNode }) { + return ( +
+ {children} +
+ ); +} + +export const UsageGuidance: Story = { + name: 'Usage guidance', + parameters: { + // The Don't example fails contrast on purpose; keep the check on for every real chip. + a11y: { + config: { + rules: [{ id: 'color-contrast', selector: '*:not([data-dont-example])' }], + }, + }, + }, + render: () => ( +
+ + } onRemove={() => {}} /> + + + {/* Deliberately wrong: approximates a product chip reported with low contrast. */} + + + + +
+ + } /> + + +
+
+ + + } onRemove={() => {}} /> + + +
+ ), +}; + +const THEMES = [ + { className: 'light', label: 'Light' }, + { className: 'light-hc', label: 'Light high contrast' }, + { className: 'dark', label: 'Dark' }, + { className: 'dark-hc', label: 'Dark high contrast' }, + { className: 'future-light', label: 'Future light' }, + { className: 'future-dark', label: 'Future dark' }, +] as const; + +export const Themes: Story = { + parameters: { + docs: { + description: { + story: `Every theme on one page, for review and visual comparison. Valid chips use ${getReferenceChipColors().valid.background}; invalid chips use ${getReferenceChipColors().invalid.background}.`, + }, + }, + }, + render: () => ( +
+ {THEMES.map((theme) => ( +
+

{theme.label}

+ } onRemove={() => {}} /> + } selected onRemove={() => {}} /> + } invalid onRemove={() => {}} /> +
+ ))} +
+ ), +}; diff --git a/packages/apollo-wind/src/components/ui/reference-chip.test.tsx b/packages/apollo-wind/src/components/ui/reference-chip.test.tsx new file mode 100644 index 0000000000..c5b4b7b1ba --- /dev/null +++ b/packages/apollo-wind/src/components/ui/reference-chip.test.tsx @@ -0,0 +1,101 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { axe } from 'jest-axe'; +import { describe, expect, it, vi } from 'vitest'; +import { ReferenceChip } from './reference-chip'; +import { getReferenceChipColors } from './reference-chip-colors'; + +describe('ReferenceChip', () => { + it('removes by keyboard without activating the containing picker or submitting a form', async () => { + const user = userEvent.setup(); + const remove = vi.fn(); + const submit = vi.fn((event) => event.preventDefault()); + const hostClick = vi.fn(); + render( +
+ + + ); + await user.tab(); + expect(screen.getByRole('button', { name: 'Clear connection' })).toHaveFocus(); + await user.keyboard('{Enter}'); + expect(remove).toHaveBeenCalledOnce(); + expect(hostClick).not.toHaveBeenCalled(); + expect(submit).not.toHaveBeenCalled(); + }); + + it('preserves editor focus and does not select the chip when removing with the mouse', async () => { + const user = userEvent.setup(); + const select = vi.fn(); + const remove = vi.fn(); + render( + <> + + + + ); + screen.getByRole('textbox').focus(); + await user.click(screen.getByRole('button')); + expect(screen.getByRole('textbox')).toHaveFocus(); + expect(select).not.toHaveBeenCalled(); + expect(remove).toHaveBeenCalledOnce(); + }); + + it('does not expose removal without a handler or in read-only mode, and disables it when disabled', async () => { + const remove = vi.fn(); + const { rerender } = render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + rerender(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + rerender(); + expect(screen.getByRole('button')).toBeDisabled(); + await userEvent.click(screen.getByRole('button')); + expect(remove).not.toHaveBeenCalled(); + }); + + it('pairs background, text and icon from one semantic family', () => { + const { rerender } = render(} selected />); + const chip = screen.getByText('Connection').closest('[data-slot="reference-chip"]'); + const icon = chip?.querySelector('[aria-hidden="true"]'); + expect(chip).toHaveStyle({ + backgroundColor: 'var(--color-info-background)', + color: 'var(--color-info-text)', + outline: '2px solid var(--color-info-icon)', + }); + expect(icon).toHaveStyle({ color: 'var(--color-info-icon)' }); + + rerender(} invalid />); + expect(chip).toHaveStyle({ + backgroundColor: 'var(--color-error-background)', + color: 'var(--color-error-text)', + }); + expect(icon).toHaveStyle({ color: 'var(--color-error-icon)' }); + }); + + it('never uses primary or accent tokens', () => { + const colors = Object.values(getReferenceChipColors()).flatMap(Object.values); + for (const value of colors) { + expect(value).toMatch(/^var\(--color-(info|error)-/); + } + }); + + it('has no accessibility violations with a custom decorative icon', async () => { + const { container } = render( + + Mail + + } + onRemove={() => {}} + /> + ); + expect(await axe(container)).toHaveNoViolations(); + }); +}); diff --git a/packages/apollo-wind/src/components/ui/reference-chip.tsx b/packages/apollo-wind/src/components/ui/reference-chip.tsx new file mode 100644 index 0000000000..e9b2842df9 --- /dev/null +++ b/packages/apollo-wind/src/components/ui/reference-chip.tsx @@ -0,0 +1,106 @@ +import { forwardRef, type HTMLAttributes, type ReactNode } from 'react'; +import { cn } from '@/lib'; +import { getReferenceChipColors } from './reference-chip-colors'; + +export interface ReferenceChipProps extends Omit, 'children'> { + /** Display name of the referenced variable, resource or connection. */ + label: string; + /** Decorative icon. Branded icons may preserve their original colors. */ + icon?: ReactNode; + onRemove?: () => void; + /** Localized accessible name for the remove button. */ + removeLabel?: string; + readOnly?: boolean; + disabled?: boolean; + invalid?: boolean; + /** Visual selection only. The containing editor or picker owns selection semantics. */ + selected?: boolean; +} + +/** Shared visual primitive. Picker behavior and editor selection remain with the host. */ +export const ReferenceChip = forwardRef( + ( + { + label, + icon, + onRemove, + removeLabel, + readOnly = false, + disabled = false, + invalid = false, + selected = false, + className, + style, + ...props + }, + ref + ) => { + const colors = getReferenceChipColors()[invalid ? 'invalid' : 'valid']; + return ( + + {icon && ( + + )} + + {label} + + {onRemove && !readOnly && ( + + )} + + ); + } +); +ReferenceChip.displayName = 'ReferenceChip'; diff --git a/packages/apollo-wind/src/components/ui/variable-value-control.tsx b/packages/apollo-wind/src/components/ui/variable-value-control.tsx index 1194c8c33f..c236d022b7 100644 --- a/packages/apollo-wind/src/components/ui/variable-value-control.tsx +++ b/packages/apollo-wind/src/components/ui/variable-value-control.tsx @@ -1,3 +1,4 @@ +import { Variable } from 'lucide-react'; import * as React from 'react'; import { joinIds } from '@/components/forms/aria-ids'; import { @@ -6,6 +7,7 @@ import { } from '@/components/ui/field-addons/value-mode-strings'; import { InputGroupPopoverTrigger } from '@/components/ui/input-group'; import { Popover, PopoverContent } from '@/components/ui/popover'; +import { ReferenceChip } from '@/components/ui/reference-chip'; import { VariablePickerContent, type VariablePickerItem } from '@/components/ui/variable-picker'; import { composeRefs } from '@/lib'; @@ -99,7 +101,7 @@ const VariableValueControl = React.forwardRef - {reference && {reference}} + {reference && } />}