Skip to content

Commit 1aa6ae9

Browse files
refactor: restore typing and extract reusable AffordanceButtons component
1 parent 42099cd commit 1aa6ae9

File tree

5 files changed

+204
-269
lines changed

5 files changed

+204
-269
lines changed

src/components/TDViewer/components/Action.tsx

Lines changed: 61 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
*
1111
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
1212
********************************************************************************/
13-
import React, { useContext, useState } from "react";
14-
import { Trash2, Copy } from "react-feather";
13+
import React, { useContext, useState, useRef } from "react";
1514
import ediTDorContext from "../../../context/ediTDorContext";
1615
import {
1716
buildAttributeListObject,
@@ -23,153 +22,109 @@ import { getFormsTooltipContent } from "../../../utils/TooltipMapper";
2322
import Form from "./Form";
2423
import AddFormElement from "../base/AddFormElement";
2524
import { copyAffordance } from "../../../utils/copyAffordance";
26-
25+
import AffordanceButtons from "./AffordanceButtons";
2726
const alreadyRenderedKeys = ["title", "forms", "description"];
2827

29-
const Action: React.FC<any> = (props) => {
28+
interface IAction {
29+
action: any;
30+
actionName: string;
31+
}
32+
const Action: React.FC<IAction> = ({ action, actionName }) => {
3033
const context = useContext(ediTDorContext);
3134
const [isExpanded, setIsExpanded] = useState(false);
32-
33-
const addFormDialog = React.useRef<any>();
34-
35-
const handleOpenAddFormDialog = () => {
36-
addFormDialog.current?.openModal();
37-
};
38-
39-
if (
40-
Object.keys(props.action).length === 0 &&
41-
props.action.constructor !== Object
42-
) {
43-
return (
44-
<div className="text-3xl text-white">
45-
Action could not be rendered because mandatory fields are missing.
46-
</div>
47-
);
48-
}
49-
50-
const action = props.action;
51-
const forms = separateForms(props.action.forms);
52-
35+
const addFormDialog = useRef<{ openModal: () => void }>(null);
36+
const forms = separateForms(action.forms);
5337
const attributeListObject = buildAttributeListObject(
54-
{ name: props.actionName },
55-
props.action,
38+
{ name: actionName },
39+
action,
5640
alreadyRenderedKeys
5741
);
58-
59-
const attributes = Object.keys(attributeListObject).map((x) => (
60-
<li key={x}>
61-
{x} : {JSON.stringify(attributeListObject[x])}
62-
</li>
63-
));
64-
65-
const handleDeleteAction = () => {
66-
context.removeOneOfAKindReducer("actions", props.actionName);
42+
const handleDelete = () => {
43+
context.removeOneOfAKindReducer("actions", actionName);
6744
};
68-
69-
const handleCopyAction = () => {
70-
try {
71-
const { updatedTD, newName } = copyAffordance({
72-
parsedTD: context.parsedTD,
73-
section: "actions",
74-
originalName: props.actionName,
75-
affordance: action,
76-
});
77-
78-
context.updateOfflineTD(JSON.stringify(updatedTD, null, 2));
79-
80-
setIsExpanded(true);
81-
82-
setTimeout(() => {
83-
document.getElementById(`action-${newName}`)?.scrollIntoView({
84-
behavior: "smooth",
85-
block: "center",
86-
});
87-
}, 100);
88-
} catch (e) {
89-
console.error(e);
90-
}
45+
const handleCopy = () => {
46+
const { updatedTD, newName } = copyAffordance({
47+
parsedTD: context.parsedTD,
48+
section: "actions",
49+
originalName: actionName,
50+
affordance: action,
51+
});
52+
context.updateOfflineTD(JSON.stringify(updatedTD, null, 2));
53+
setIsExpanded(true);
54+
setTimeout(() => {
55+
document
56+
.getElementById(`action-${newName}`)
57+
?.scrollIntoView({ behavior: "smooth", block: "center" });
58+
}, 100);
9159
};
9260

9361
return (
9462
<details
95-
id={`action-${props.actionName}`}
96-
className="mb-1"
63+
id={`action-${actionName}`}
64+
className={`mb-2 ${isExpanded ? "overflow-hidden rounded-lg bg-gray-500" : ""}`}
9765
open={isExpanded}
9866
onToggle={() => setIsExpanded(!isExpanded)}
9967
>
100-
<summary
101-
className={`flex cursor-pointer items-center rounded-t-lg pl-2 text-xl font-bold text-white ${
102-
isExpanded ? "bg-gray-500" : ""
103-
}`}
104-
>
105-
<h3 className="flex-grow px-2">{action.title ?? props.actionName}</h3>
68+
<summary className="flex cursor-pointer items-center py-1 pl-2 text-xl font-bold text-white">
69+
<h3 className="flex-grow px-2">{action.title ?? actionName}</h3>
10670

10771
{isExpanded && (
108-
<>
109-
<button
110-
className="flex h-10 w-10 items-center justify-center self-stretch rounded-bl-lg bg-gray-400 text-base"
111-
title="Copy action"
112-
onClick={(e) => {
113-
e.preventDefault();
114-
e.stopPropagation();
115-
handleCopyAction();
116-
}}
117-
>
118-
<Copy size={16} color="white" />
119-
</button>
120-
<button
121-
className="flex h-10 w-10 items-center justify-center self-stretch rounded-bl-lg rounded-tr-lg bg-gray-400 text-base"
122-
title="Delete action"
123-
onClick={(e) => {
124-
e.preventDefault();
125-
e.stopPropagation();
126-
handleDeleteAction();
127-
}}
128-
>
129-
<Trash2 size={16} color="white" />
130-
</button>
131-
</>
72+
<AffordanceButtons
73+
copyTitle="Copy action"
74+
deleteTitle="Delete action"
75+
onCopy={(e) => {
76+
e.preventDefault();
77+
e.stopPropagation();
78+
handleCopy();
79+
}}
80+
onDelete={(e) => {
81+
e.preventDefault();
82+
e.stopPropagation();
83+
handleDelete();
84+
}}
85+
/>
13286
)}
13387
</summary>
13488

135-
<div className="mb-4 rounded-b-lg bg-gray-500 px-2 pb-4">
89+
<div className="px-2 pb-4">
13690
{action.description && (
13791
<div className="px-2 pb-2 text-lg text-gray-400">
13892
{action.description}
13993
</div>
14094
)}
14195

142-
<ul className="list-disc pl-6 text-base text-gray-300">{attributes}</ul>
96+
<ul className="list-disc pl-6 text-base text-gray-300">
97+
{Object.entries(attributeListObject).map(([k, v]) => (
98+
<li key={k}>
99+
{k}: {JSON.stringify(v)}
100+
</li>
101+
))}
102+
</ul>
143103

144-
<div className="flex items-center justify-start pb-2 pt-2">
145-
<InfoIconWrapper
146-
className="flex-grow"
147-
tooltip={getFormsTooltipContent()}
148-
id="actions"
149-
>
150-
<h4 className="pr-1 text-lg font-bold text-white">Forms</h4>
151-
</InfoIconWrapper>
152-
</div>
104+
<InfoIconWrapper tooltip={getFormsTooltipContent()} id="actions">
105+
<h4 className="text-lg font-bold text-white">Forms</h4>
106+
</InfoIconWrapper>
153107

154-
<AddFormElement onClick={handleOpenAddFormDialog} />
108+
<AddFormElement onClick={() => addFormDialog.current?.openModal()} />
155109

156110
<AddFormDialog
157111
type="action"
158112
interaction={action}
159-
interactionName={props.actionName}
113+
interactionName={actionName}
160114
ref={addFormDialog}
161115
/>
162116

163117
{forms.map((form, i) => (
164118
<Form
165119
key={`${i}-${form?.href ?? "nohref"}`}
166120
form={form}
167-
propName={props.actionName}
121+
propName={actionName}
168122
interactionType="action"
169123
/>
170124
))}
171125
</div>
172126
</details>
173127
);
174128
};
175-
export default Action;
129+
130+
export default Action;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/********************************************************************************
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License v. 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
10+
*
11+
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
12+
********************************************************************************/
13+
import React from "react";
14+
import { Copy, Trash2 } from "react-feather";
15+
16+
interface Props {
17+
onCopy: (e: React.MouseEvent) => void;
18+
onDelete: (e: React.MouseEvent) => void;
19+
copyTitle: string;
20+
deleteTitle: string;
21+
}
22+
23+
const AffordanceButtons: React.FC<Props> = ({
24+
onCopy,
25+
onDelete,
26+
copyTitle,
27+
deleteTitle,
28+
}) => {
29+
return (
30+
<div className="flex self-stretch">
31+
<button
32+
className="flex w-14 items-center justify-center bg-gray-400 transition-colors hover:bg-gray-500"
33+
title={copyTitle}
34+
onClick={onCopy}
35+
>
36+
<Copy size={20} color="white" />
37+
</button>
38+
39+
<button
40+
className="flex w-14 items-center justify-center rounded-tr-lg border-l border-gray-500 bg-gray-400 transition-colors hover:bg-gray-500"
41+
title={deleteTitle}
42+
onClick={onDelete}
43+
>
44+
<Trash2 size={20} color="white" />
45+
</button>
46+
</div>
47+
);
48+
};
49+
50+
export default AffordanceButtons;

0 commit comments

Comments
 (0)