Skip to content

Commit c92bfab

Browse files
Add copy button for TD affordances (actions, properties, events)
1 parent 72623d9 commit c92bfab

3 files changed

Lines changed: 229 additions & 75 deletions

File tree

src/components/TDViewer/components/Action.tsx

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
1212
********************************************************************************/
1313
import React, { useContext, useState } from "react";
14-
import { Trash2 } from "react-feather";
14+
import { Trash2, Copy } from "react-feather";
1515
import ediTDorContext from "../../../context/ediTDorContext";
1616
import {
1717
buildAttributeListObject,
@@ -27,10 +27,9 @@ const alreadyRenderedKeys = ["title", "forms", "description"];
2727

2828
const Action: React.FC<any> = (props) => {
2929
const context = useContext(ediTDorContext);
30-
3130
const [isExpanded, setIsExpanded] = useState(false);
3231

33-
const addFormDialog = React.useRef();
32+
const addFormDialog = React.useRef<any>();
3433
const handleOpenAddFormDialog = () => {
3534
addFormDialog.current.openModal();
3635
};
@@ -54,36 +53,86 @@ const Action: React.FC<any> = (props) => {
5453
props.action,
5554
alreadyRenderedKeys
5655
);
57-
const attributes = Object.keys(attributeListObject).map((x) => {
58-
return (
59-
<li key={x}>
60-
{x} : {JSON.stringify(attributeListObject[x])}
61-
</li>
62-
);
63-
});
56+
57+
const attributes = Object.keys(attributeListObject).map((x) => (
58+
<li key={x}>
59+
{x} : {JSON.stringify(attributeListObject[x])}
60+
</li>
61+
));
6462

6563
const handleDeleteAction = () => {
6664
context.removeOneOfAKindReducer("actions", props.actionName);
6765
};
6866

67+
const handleCopyAction = () => {
68+
const parsedTD = context.parsedTD;
69+
if (!parsedTD || !parsedTD.actions) {
70+
console.error("parsedTD or actions missing", parsedTD);
71+
return;
72+
}
73+
const originalName = props.actionName;
74+
let newName = `${originalName}_copy`;
75+
let counter = 1;
76+
while (parsedTD.actions[newName]) {
77+
newName = `${originalName}_copy_${counter++}`;
78+
}
79+
const copiedAction = structuredClone(action);
80+
if (copiedAction.title) {
81+
copiedAction.title = `${copiedAction.title} copy`;
82+
}
83+
const updatedParsedTD = {
84+
...parsedTD,
85+
actions: {
86+
...parsedTD.actions,
87+
[newName]: copiedAction,
88+
},
89+
};
90+
context.updateOfflineTD(JSON.stringify(updatedParsedTD, null, 2));
91+
};
92+
6993
return (
7094
<details
7195
className="mb-1"
7296
open={isExpanded}
7397
onToggle={() => setIsExpanded(!isExpanded)}
7498
>
7599
<summary
76-
className={`flex cursor-pointer items-center rounded-t-lg pl-2 text-xl font-bold text-white ${isExpanded ? "bg-gray-500" : ""}`}
100+
className={`flex cursor-pointer items-center rounded-t-lg pl-2 text-xl font-bold text-white ${
101+
isExpanded ? "bg-gray-500" : ""
102+
}`}
77103
>
78-
<h3 className="flex-grow px-2">{action.title ?? props.actionName}</h3>
104+
<h3 className="flex-grow px-2">
105+
{action.title ?? props.actionName}
106+
</h3>
107+
79108
{isExpanded && (
80-
<button
81-
className="flex h-10 w-10 items-center justify-center self-stretch rounded-bl-md rounded-tr-md bg-gray-400 text-base"
82-
onClick={handleDeleteAction}
83-
>
84-
<Trash2 size={16} color="white" />
85-
</button>
86-
)}
109+
<>
110+
<button
111+
className="flex h-10 w-10 items-center justify-center self-stretch bg-gray-400 text-base"
112+
title="Copy action"
113+
onClick={(e) => {
114+
e.preventDefault();
115+
e.stopPropagation();
116+
handleCopyAction();
117+
}}
118+
>
119+
<Copy size={16} color="white" />
120+
</button>
121+
122+
<button
123+
className="flex h-10 w-10 items-center justify-center self-stretch rounded-bl-md rounded-tr-md bg-gray-400 text-base"
124+
title="Delete action"
125+
onClick={(e) => {
126+
e.preventDefault();
127+
e.stopPropagation();
128+
handleDeleteAction();
129+
}}
130+
>
131+
<Trash2 size={16} color="white" />
132+
</button>
133+
</>
134+
)}
135+
87136
</summary>
88137

89138
<div className="mb-4 rounded-b-lg bg-gray-500 px-2 pb-4">
@@ -92,7 +141,10 @@ const Action: React.FC<any> = (props) => {
92141
{action.description}
93142
</div>
94143
)}
95-
<ul className="list-disc pl-6 text-base text-gray-300">{attributes}</ul>
144+
145+
<ul className="list-disc pl-6 text-base text-gray-300">
146+
{attributes}
147+
</ul>
96148

97149
<div className="flex items-center justify-start pb-2 pt-2">
98150
<InfoIconWrapper
@@ -105,19 +157,21 @@ const Action: React.FC<any> = (props) => {
105157
</div>
106158

107159
<AddFormElement onClick={handleOpenAddFormDialog} />
160+
108161
<AddFormDialog
109-
type={"action"}
162+
type="action"
110163
interaction={action}
111164
interactionName={props.actionName}
112165
ref={addFormDialog}
113166
/>
167+
114168
{forms.map((form, i) => (
115169
<Form
116170
key={`${i}-${form.href}`}
117171
form={form}
118172
propName={props.actionName}
119-
interactionType={"action"}
120-
></Form>
173+
interactionType="action"
174+
/>
121175
))}
122176
</div>
123177
</details>

src/components/TDViewer/components/Event.tsx

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
*
1111
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
1212
********************************************************************************/
13-
import React, { useContext, useState } from "react";
14-
import { Trash2 } from "react-feather";
13+
import React, { useContext, useState, useRef } from "react";
14+
import { Trash2, Copy } from "react-feather";
1515
import ediTDorContext from "../../../context/ediTDorContext";
1616
import {
1717
buildAttributeListObject,
@@ -27,12 +27,11 @@ const alreadyRenderedKeys = ["title", "forms", "description"];
2727

2828
const Event: React.FC<any> = (props) => {
2929
const context = useContext(ediTDorContext);
30-
3130
const [isExpanded, setIsExpanded] = useState(false);
3231

33-
const addFormDialog = React.useRef();
32+
const addFormDialog = useRef<{ openModal: () => void }>(null);
3433
const handleOpenAddFormDialog = () => {
35-
addFormDialog.current.openModal();
34+
addFormDialog.current?.openModal();
3635
};
3736

3837
if (
@@ -48,22 +47,47 @@ const Event: React.FC<any> = (props) => {
4847

4948
const event = props.event;
5049
const forms = separateForms(props.event.forms);
50+
5151
const attributeListObject = buildAttributeListObject(
5252
{ name: props.eventName },
5353
props.event,
5454
alreadyRenderedKeys
5555
);
56-
const attributes = Object.keys(attributeListObject).map((x) => {
57-
return (
58-
<li key={x}>
59-
{x} : {JSON.stringify(attributeListObject[x])}
60-
</li>
61-
);
62-
});
6356

57+
const attributes = Object.keys(attributeListObject).map((x) => (
58+
<li key={x}>
59+
{x} : {JSON.stringify(attributeListObject[x])}
60+
</li>
61+
));
62+
6463
const handleDeleteEventClicked = () => {
6564
context.removeOneOfAKindReducer("events", props.eventName);
6665
};
66+
const handleCopyEvent = () => {
67+
const parsedTD = context.parsedTD;
68+
if (!parsedTD || !parsedTD.events) {
69+
console.error("parsedTD or events missing", parsedTD);
70+
return;
71+
}
72+
const originalName = props.eventName;
73+
let newName = `${originalName}_copy`;
74+
let counter = 1;
75+
while (parsedTD.events[newName]) {
76+
newName = `${originalName}_copy_${counter++}`;
77+
}
78+
const copiedEvent = structuredClone(event);
79+
const baseTitle = event.title ?? props.eventName;
80+
copiedEvent.title = `${baseTitle} copy`;
81+
const updatedParsedTD = {
82+
...parsedTD,
83+
events: {
84+
...parsedTD.events,
85+
[newName]: copiedEvent,
86+
},
87+
};
88+
89+
context.updateOfflineTD(JSON.stringify(updatedParsedTD, null, 2));
90+
};
6791

6892
return (
6993
<details
@@ -72,16 +96,39 @@ const Event: React.FC<any> = (props) => {
7296
onToggle={() => setIsExpanded(!isExpanded)}
7397
>
7498
<summary
75-
className={`flex cursor-pointer items-center rounded-t-lg pl-2 text-xl font-bold text-white ${isExpanded ? "bg-gray-500" : ""}`}
99+
className={`flex cursor-pointer items-center rounded-t-lg pl-2 text-xl font-bold text-white ${
100+
isExpanded ? "bg-gray-500" : ""
101+
}`}
76102
>
77-
<div className="flex-grow px-2">{event.title ?? props.eventName}</div>
103+
<div className="flex-grow px-2">
104+
{event.title ?? props.eventName}
105+
</div>
106+
78107
{isExpanded && (
79-
<button
80-
className="flex h-10 w-10 items-center justify-center self-stretch rounded-bl-md rounded-tr-md bg-gray-400 text-base"
81-
onClick={handleDeleteEventClicked}
82-
>
83-
<Trash2 size={16} color="white" />
84-
</button>
108+
<>
109+
<button
110+
className="flex h-10 w-10 items-center justify-center self-stretch bg-gray-400 text-base"
111+
title="Copy event"
112+
onClick={(e) => {
113+
e.preventDefault();
114+
e.stopPropagation();
115+
handleCopyEvent();
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-md rounded-tr-md bg-gray-400 text-base"
122+
title="Delete event"
123+
onClick={(e) => {
124+
e.preventDefault();
125+
e.stopPropagation();
126+
handleDeleteEventClicked();
127+
}}
128+
>
129+
<Trash2 size={16} color="white" />
130+
</button>
131+
</>
85132
)}
86133
</summary>
87134

@@ -91,7 +138,9 @@ const Event: React.FC<any> = (props) => {
91138
{event.description}
92139
</div>
93140
)}
94-
<ul className="list-disc pl-6 text-base text-gray-300">{attributes}</ul>
141+
<ul className="list-disc pl-6 text-base text-gray-300">
142+
{attributes}
143+
</ul>
95144

96145
<div className="flex items-center justify-start pb-2 pt-2">
97146
<InfoIconWrapper
@@ -102,10 +151,9 @@ const Event: React.FC<any> = (props) => {
102151
<h4 className="pr-1 text-lg font-bold text-white">Forms</h4>
103152
</InfoIconWrapper>
104153
</div>
105-
106154
<AddFormElement onClick={handleOpenAddFormDialog} />
107155
<AddFormDialog
108-
type={"event"}
156+
type="event"
109157
interaction={event}
110158
interactionName={props.eventName}
111159
ref={addFormDialog}
@@ -115,8 +163,8 @@ const Event: React.FC<any> = (props) => {
115163
key={`${i}-${form.href}`}
116164
form={form}
117165
propName={props.eventName}
118-
interactionType={"event"}
119-
></Form>
166+
interactionType="event"
167+
/>
120168
))}
121169
</div>
122170
</details>

0 commit comments

Comments
 (0)