diff --git a/packages/apollo-react/package.json b/packages/apollo-react/package.json index b046bcd32..465cfb170 100644 --- a/packages/apollo-react/package.json +++ b/packages/apollo-react/package.json @@ -186,6 +186,7 @@ "@lexical/table": "0.42.0", "@lexical/utils": "0.42.0", "@lingui/core": "^5.6.1", + "@lingui/message-utils": "^5.6.1", "@lingui/react": "^5.6.1", "@mui/icons-material": "^5.18.0", "@mui/material": "^5.18.0", diff --git a/packages/apollo-react/src/canvas/components/JsonTree/DecorationChip.tsx b/packages/apollo-react/src/canvas/components/JsonTree/DecorationChip.tsx new file mode 100644 index 000000000..8bb48842b --- /dev/null +++ b/packages/apollo-react/src/canvas/components/JsonTree/DecorationChip.tsx @@ -0,0 +1,53 @@ +import { cn } from '@uipath/apollo-wind'; +import { CanvasTooltip } from '../CanvasTooltip'; +import type { NodeDecoration, NodeDecorationTone } from './JsonTree.types'; + +const CHIP_TONE_TEXT_CLASS: Record = { + neutral: 'text-foreground-subtle', + info: 'text-info', + warning: 'text-warning', + error: 'text-error', +}; + +const CHIP_TONE_BORDER_CLASS: Record = { + neutral: 'border-border', + info: 'border-info/40', + warning: 'border-warning/40', + error: 'border-error/40', +}; + +export function DecorationChip({ decoration }: { decoration?: NodeDecoration }) { + const chip = decoration?.chip; + if (!chip || (chip.label == null && chip.icon == null)) return null; + const tone = chip.tone ?? 'neutral'; + + // A label gets the pill treatment; a bare icon renders unboxed. + const content = chip.label ? ( + + {chip.icon && {chip.icon}} + {chip.label} + + ) : ( + + {chip.icon} + + ); + + if (!chip.tooltip) return content; + return ( + {chip.tooltip}}> + {content} + + ); +} diff --git a/packages/apollo-react/src/canvas/components/JsonTree/EditorChrome.tsx b/packages/apollo-react/src/canvas/components/JsonTree/EditorChrome.tsx new file mode 100644 index 000000000..c72cb045a --- /dev/null +++ b/packages/apollo-react/src/canvas/components/JsonTree/EditorChrome.tsx @@ -0,0 +1,91 @@ +import { Button, cn } from '@uipath/apollo-wind'; +import { useEffect, useRef } from 'react'; +import { useSafeLingui } from '../../../i18n'; + +/** Apply/Cancel row shared by the multiline editors. */ +export function EditorActions({ + onApply, + onCancel, + applyDisabled = false, +}: { + onApply: () => void; + onCancel: () => void; + applyDisabled?: boolean; +}) { + const { _ } = useSafeLingui(); + return ( +
+ + +
+ ); +} + +export interface EditorTextareaProps { + value: string; + onChange: (value: string) => void; + /** Bound to Ctrl/Cmd+Enter. */ + onApply: () => void; + /** Bound to Escape (contained so a host dialog does not also close). */ + onCancel: () => void; + invalid: boolean; + rows: number; + ariaLabel: string; + placeholder?: string; + className?: string; +} + +/** Multiline editing surface shared by the leaf and container editors. */ +export function EditorTextarea({ + value, + onChange, + onApply, + onCancel, + invalid, + rows, + ariaLabel, + placeholder, + className, +}: EditorTextareaProps) { + const textareaRef = useRef(null); + + // Mounted in response to the user's edit action; focus follows it (the + // attribute form trips a11y linting). + useEffect(() => { + textareaRef.current?.focus(); + }, []); + + return ( +