Skip to content

Commit f2cfc9c

Browse files
committed
Rework Task Action Buttons into Action Framework
1 parent 92df4ce commit f2cfc9c

File tree

6 files changed

+225
-277
lines changed

6 files changed

+225
-277
lines changed

src/components/shared/ContextPanel/Blocks/ActionBlock.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type Action = {
2121
);
2222

2323
// Temporary: ReactNode included for backward compatibility with some existing buttons. In the long-term we should strive for only Action types.
24-
export type ActionOrReactNode = Action | ReactNode;
24+
type ActionOrReactNode = Action | ReactNode;
2525

2626
interface ActionBlockProps {
2727
title?: string;

src/components/shared/Dialogs/ComponentDetailsDialog.tsx

Lines changed: 31 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ import { useHydrateComponentReference } from "@/hooks/useHydrateComponentReferen
1717
import type { ComponentReference } from "@/utils/componentSpec";
1818

1919
import InfoIconButton from "../Buttons/InfoIconButton";
20-
import TooltipButton from "../Buttons/TooltipButton";
21-
import { ComponentEditorDialog } from "../ComponentEditor/ComponentEditorDialog";
2220
import { ComponentFavoriteToggle } from "../FavoriteComponentToggle";
2321
import { InfoBox } from "../InfoBox";
2422
import { PublishComponent } from "../ManageComponent/PublishComponent";
@@ -32,7 +30,6 @@ interface ComponentDetailsProps {
3230
component: ComponentReference;
3331
displayName: string;
3432
trigger?: ReactNode;
35-
actions?: ReactNode[];
3633
onClose?: () => void;
3734
onDelete?: () => void;
3835
}
@@ -64,12 +61,7 @@ const ComponentDetailsDialogContentSkeleton = () => {
6461
};
6562

6663
const ComponentDetailsDialogContent = withSuspenseWrapper(
67-
({
68-
component,
69-
displayName,
70-
actions = [],
71-
onDelete,
72-
}: ComponentDetailsProps) => {
64+
({ component, displayName, onDelete }: ComponentDetailsProps) => {
7365
const remoteComponentLibrarySearchEnabled = useBetaFlagValue(
7466
"remote-component-library-search",
7567
);
@@ -138,7 +130,6 @@ const ComponentDetailsDialogContent = withSuspenseWrapper(
138130
componentSpec={componentSpec}
139131
componentDigest={componentDigest}
140132
url={url}
141-
actions={actions}
142133
onDelete={onDelete}
143134
/>
144135
</TabsContent>
@@ -175,18 +166,12 @@ const ComponentDetails = ({
175166
component,
176167
displayName,
177168
trigger,
178-
actions = [],
179169
onClose,
180170
onDelete,
181171
}: ComponentDetailsProps) => {
182-
const hasEnabledInAppEditor = useBetaFlagValue("in-app-component-editor");
183-
184-
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
185172
const [open, setOpen] = useState(false);
186173
const dialogTriggerButton = trigger || <InfoIconButton />;
187174

188-
const componentText = component.text;
189-
190175
const dialogContextValue = useMemo(
191176
() => ({
192177
name: "ComponentDetails",
@@ -197,79 +182,45 @@ const ComponentDetails = ({
197182
[],
198183
);
199184

200-
const handleCloseEditDialog = useCallback(() => {
201-
setIsEditDialogOpen(false);
202-
}, []);
203-
204185
const onOpenChange = useCallback((open: boolean) => {
205186
setOpen(open);
206187
if (!open) {
207188
onClose?.();
208189
}
209190
}, []);
210191

211-
const handleEditComponent = useCallback(() => {
212-
setIsEditDialogOpen(true);
213-
}, []);
214-
215-
const actionsWithEdit = useMemo(() => {
216-
if (!hasEnabledInAppEditor) return actions;
217-
218-
const EditButton = (
219-
<TooltipButton
220-
variant="secondary"
221-
onClick={handleEditComponent}
222-
tooltip="Edit Component Definition"
223-
key={`${displayName}-edit-button`}
224-
>
225-
<Icon name="FilePenLine" />
226-
</TooltipButton>
227-
);
228-
229-
return [...actions, EditButton];
230-
}, [actions, hasEnabledInAppEditor, handleEditComponent]);
231-
232192
return (
233-
<>
234-
<Dialog modal open={open} onOpenChange={onOpenChange}>
235-
<DialogTrigger asChild>{dialogTriggerButton}</DialogTrigger>
193+
<Dialog modal open={open} onOpenChange={onOpenChange}>
194+
<DialogTrigger asChild>{dialogTriggerButton}</DialogTrigger>
236195

237-
<DialogDescription
238-
className="hidden"
239-
aria-label={`${displayName} component details`}
240-
>
241-
{`${displayName} component details`}
242-
</DialogDescription>
243-
<DialogContent
244-
className="max-w-2xl min-w-2xl overflow-hidden"
245-
aria-label={`${displayName} component details`}
246-
>
247-
<DialogHeader>
248-
<DialogTitle className="flex items-center gap-2 mr-5">
249-
<span>{displayName}</span>
250-
<ComponentFavoriteToggle component={component} />
251-
</DialogTitle>
252-
</DialogHeader>
253-
254-
<DialogContext.Provider value={dialogContextValue}>
255-
<ComponentDetailsDialogContent
256-
component={component}
257-
displayName={displayName}
258-
trigger={dialogTriggerButton}
259-
actions={actionsWithEdit}
260-
onClose={onClose}
261-
onDelete={onDelete}
262-
/>
263-
</DialogContext.Provider>
264-
</DialogContent>
265-
</Dialog>
266-
{isEditDialogOpen && (
267-
<ComponentEditorDialog
268-
text={componentText}
269-
onClose={handleCloseEditDialog}
270-
/>
271-
)}
272-
</>
196+
<DialogDescription
197+
className="hidden"
198+
aria-label={`${displayName} component details`}
199+
>
200+
{`${displayName} component details`}
201+
</DialogDescription>
202+
<DialogContent
203+
className="max-w-2xl min-w-2xl overflow-hidden"
204+
aria-label={`${displayName} component details`}
205+
>
206+
<DialogHeader>
207+
<DialogTitle className="flex items-center gap-2 mr-5">
208+
<span>{displayName}</span>
209+
<ComponentFavoriteToggle component={component} />
210+
</DialogTitle>
211+
</DialogHeader>
212+
213+
<DialogContext.Provider value={dialogContextValue}>
214+
<ComponentDetailsDialogContent
215+
component={component}
216+
displayName={displayName}
217+
trigger={dialogTriggerButton}
218+
onClose={onClose}
219+
onDelete={onDelete}
220+
/>
221+
</DialogContext.Provider>
222+
</DialogContent>
223+
</Dialog>
273224
);
274225
};
275226

src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeCard.tsx

Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useNavigate } from "@tanstack/react-router";
22
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
33

4-
import type { TooltipButtonProps } from "@/components/shared/Buttons/TooltipButton";
5-
import { ComponentEditorDialog } from "@/components/shared/ComponentEditor/ComponentEditorDialog";
4+
import { CodeViewer } from "@/components/shared/CodeViewer";
5+
import type { Action } from "@/components/shared/ContextPanel/Blocks/ActionBlock";
66
import { PublishedComponentBadge } from "@/components/shared/ManageComponent/PublishedComponentBadge";
77
import { trimDigest } from "@/components/shared/ManageComponent/utils/digest";
88
import { useBetaFlagValue } from "@/components/shared/Settings/useBetaFlags";
@@ -36,7 +36,6 @@ const TaskNodeCard = () => {
3636
"remote-component-library-search",
3737
);
3838
const isSubgraphNavigationEnabled = useBetaFlagValue("subgraph-navigation");
39-
const isInAppEditorEnabled = useBetaFlagValue("in-app-component-editor");
4039
const { registerNode } = useNodesOverlay();
4140
const taskNode = useTaskNode();
4241
const {
@@ -52,7 +51,7 @@ const TaskNodeCard = () => {
5251
const nodeRef = useRef<HTMLDivElement | null>(null);
5352
const contentRef = useRef<HTMLDivElement>(null);
5453

55-
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
54+
const [isYamlFullscreen, setIsYamlFullscreen] = useState(false);
5655
const [updateOverlayDialogOpen, setUpdateOverlayDialogOpen] = useState<
5756
UpdateOverlayMessage["data"] | undefined
5857
>();
@@ -113,66 +112,64 @@ const TaskNodeCard = () => {
113112
}
114113
}, []);
115114

116-
const handleEditComponent = useCallback(() => {
117-
setIsEditDialogOpen(true);
118-
}, []);
119-
120-
const handleCloseEditDialog = useCallback(() => {
121-
setIsEditDialogOpen(false);
122-
}, []);
115+
const handleDuplicateTask = useCallback(() => {
116+
callbacks.onDuplicate?.();
117+
}, [callbacks]);
123118

124-
const taskConfigMarkup = useMemo(() => {
125-
const actions: Array<TooltipButtonProps> = [];
126-
127-
if (!readOnly) {
128-
actions.push({
129-
children: <Icon name="Copy" size="sm" />,
130-
variant: "outline",
131-
tooltip: "Duplicate Task",
132-
onClick: callbacks.onDuplicate,
133-
});
134-
}
135-
136-
if (!readOnly && !isCustomComponent) {
137-
actions.push({
138-
children: <Icon name="CircleFadingArrowUp" size="sm" />,
139-
variant: "outline",
140-
tooltip: "Update Task from Source URL",
141-
onClick: callbacks.onUpgrade,
142-
});
143-
}
119+
const handleUpgradeTask = useCallback(() => {
120+
callbacks.onUpgrade?.();
121+
}, [callbacks]);
144122

145-
if (isSubgraphNode && taskId && isSubgraphNavigationEnabled) {
146-
actions.push({
147-
children: <Icon name="Workflow" size="sm" />,
148-
variant: "outline",
149-
tooltip: `Enter Subgraph: ${subgraphDescription}`,
150-
onClick: () => navigateToSubgraph(taskId),
151-
});
152-
}
153-
154-
if (isInAppEditorEnabled) {
155-
actions.push({
156-
children: <Icon name="FilePenLine" size="sm" />,
157-
variant: "outline",
158-
tooltip: "Edit Component Definition",
159-
onClick: handleEditComponent,
160-
});
123+
const handleEnterSubgraph = useCallback(() => {
124+
if (taskId) {
125+
navigateToSubgraph(taskId);
161126
}
127+
}, [navigateToSubgraph, taskId]);
162128

163-
return <TaskOverview taskNode={taskNode} key={nodeId} actions={actions} />;
129+
const taskConfigMarkup = useMemo(() => {
130+
const customActions: Action[] = [
131+
{
132+
label: "Duplicate Task",
133+
icon: "Copy",
134+
hidden: readOnly,
135+
onClick: handleDuplicateTask,
136+
},
137+
{
138+
label: "Update Task from Source URL",
139+
icon: "CircleFadingArrowUp",
140+
hidden: readOnly || isCustomComponent,
141+
onClick: handleUpgradeTask,
142+
},
143+
{
144+
label: `Enter Subgraph: ${subgraphDescription}`,
145+
icon: "Workflow",
146+
hidden: !isSubgraphNode || !isSubgraphNavigationEnabled,
147+
onClick: handleEnterSubgraph,
148+
},
149+
{
150+
label: "View YAML",
151+
icon: "FileCodeCorner",
152+
onClick: () => setIsYamlFullscreen(true),
153+
},
154+
];
155+
156+
return (
157+
<TaskOverview
158+
key={nodeId}
159+
taskNode={taskNode}
160+
customActions={customActions}
161+
/>
162+
);
164163
}, [
165164
nodeId,
166165
readOnly,
167166
callbacks.onDuplicate,
168167
callbacks.onUpgrade,
169-
isInAppEditorEnabled,
170168
isCustomComponent,
171169
isSubgraphNode,
172170
taskId,
173171
subgraphDescription,
174172
navigateToSubgraph,
175-
handleEditComponent,
176173
]);
177174

178175
const handleInputSectionClick = useCallback(() => {
@@ -248,6 +245,8 @@ const TaskNodeCard = () => {
248245
</QuickTooltip>
249246
);
250247

248+
const componentText = taskSpec.componentRef?.text;
249+
251250
return (
252251
<>
253252
<Card
@@ -333,10 +332,13 @@ const TaskNodeCard = () => {
333332
) : null}
334333
</CardContent>
335334
</Card>
336-
{isEditDialogOpen && (
337-
<ComponentEditorDialog
338-
text={taskSpec.componentRef?.text}
339-
onClose={handleCloseEditDialog}
335+
{isYamlFullscreen && componentText && (
336+
<CodeViewer
337+
code={componentText}
338+
language="yaml"
339+
filename={name}
340+
isFullscreen={isYamlFullscreen}
341+
onClose={() => setIsYamlFullscreen(false)}
340342
/>
341343
)}
342344
</>

0 commit comments

Comments
 (0)