Skip to content

Commit 728b6bb

Browse files
Fixing of sprites duplication on copy pasting during renaming + Overall reactivity + Updating packages (npm audit fix) (#10)
* Fixing dublicating of sprites during renaming, and fixing unreactive rename behaviour
1 parent da8a121 commit 728b6bb

File tree

4 files changed

+234
-425
lines changed

4 files changed

+234
-425
lines changed

media/editor/components.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react";
22
import React, { useEffect, useRef, useState } from "react";
3+
import { useGlobalHandler } from "./useHelpers";
34

45
interface EditableFieldsProps extends React.HTMLProps<HTMLDivElement> {
56
value: string;
67
displayValue?: string;
78
label?: string;
89
onChange: (value: any) => void;
10+
onEditingStateChanged?: (value: boolean) => void;
911
}
1012

1113
// Text editable by doubleclicking it
1214
export const EditableField: React.FC<EditableFieldsProps> = props => {
13-
const { value, displayValue, label, onChange, ...rest } = props;
15+
const { value, displayValue, label, onChange, onEditingStateChanged, ...rest } = props;
1416
const rawDisplayValue = displayValue || value;
1517
const [editing, setEditing] = useState(false);
1618
const inputRef = useRef(null);
17-
19+
useEffect(() => {
20+
if (onEditingStateChanged) {
21+
onEditingStateChanged(editing);
22+
}
23+
}, [editing, onEditingStateChanged]);
1824
const pushChange = () => {
1925
props.onChange((inputRef?.current as unknown as HTMLInputElement).value);
2026
setEditing(false);
@@ -23,15 +29,34 @@ export const EditableField: React.FC<EditableFieldsProps> = props => {
2329
const toggleEdit = () => {
2430
setEditing(!editing);
2531
};
26-
32+
const enableRename = () => {
33+
setEditing(true);
34+
};
2735
useEffect(() => {
2836
if (editing) {
2937
(inputRef?.current as unknown as HTMLInputElement).focus();
3038
}
3139
}, [editing]);
3240

41+
useGlobalHandler(
42+
"keydown",
43+
(e:KeyboardEvent) => {
44+
if(editing){
45+
switch (e.code) {
46+
case "Escape":
47+
case "Enter":
48+
setEditing(false);
49+
e.preventDefault();
50+
break;
51+
}
52+
}
53+
},
54+
[editing]
55+
);
56+
3357
return (
34-
<div {...rest} onDoubleClick={toggleEdit}>
58+
<div {...rest} onDoubleClick={editing ? undefined : toggleEdit}>
59+
{!editing && rawDisplayValue}
3560
{editing && (
3661
<VSCodeTextField
3762
ref={inputRef}
@@ -40,7 +65,6 @@ export const EditableField: React.FC<EditableFieldsProps> = props => {
4065
onBlur={() => setEditing(false)}
4166
/>
4267
)}
43-
{!editing && rawDisplayValue}
4468
</div>
4569
);
4670
};

media/editor/listView.tsx

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react";
2-
import React, { useState } from "react";
2+
import React, { useEffect, useState } from "react";
33
import { DmiState, Dirs, Dmi } from "../../shared/dmi";
44
import { EditableField } from "./components";
55
import { buildClassName, useGlobalHandler } from "./useHelpers";
@@ -12,14 +12,21 @@ type ListStateDisplayProps = {
1212
selected: boolean;
1313
hidden: boolean;
1414
duplicate: boolean;
15+
editing?: boolean;
1516
delete: () => void;
1617
select: () => void;
1718
open: () => void;
19+
onEditingStateChanged?: (value: boolean) => void;
1820
modify: (modified_state: DmiState) => void;
1921
};
2022

2123
// icon state preview on the state list
2224
const ListStateDisplay: React.FC<ListStateDisplayProps> = props => {
25+
const [editing, setEditing] = useState(false);
26+
useEffect(() => {
27+
if (props.onEditingStateChanged)
28+
props.onEditingStateChanged(editing);
29+
}, [editing, props.onEditingStateChanged]);
2330
const iconState = props.state;
2431
const listClassName = buildClassName({
2532
statePreviewBox: true,
@@ -40,13 +47,16 @@ const ListStateDisplay: React.FC<ListStateDisplayProps> = props => {
4047
new_state.name = value;
4148
props.modify(new_state);
4249
};
43-
50+
const _renaming_node = (editing: boolean) => {
51+
setEditing(editing);
52+
};
4453
return (
4554
<div className={listClassName} onClick={handleClick} hidden={props.hidden}>
4655
<EditableField
4756
value={iconState.name}
4857
displayValue={displayedName}
4958
onChange={updateName}
59+
onEditingStateChanged={_renaming_node}
5060
className={nameFieldClass}
5161
/>
5262
{props.duplicate && <div className="duplicate">Duplicate</div>}
@@ -66,6 +76,7 @@ type StateListProps = {
6676

6777
export const StateList: React.FC<StateListProps> = props => {
6878
const [selectedState, setSelectedState] = useState<number | null>(null);
79+
const [renamingNode, setRenamingNode] = useState(false);
6980
const dmi = props.dmi;
7081

7182
const delete_state = (state_index: number) => () => {
@@ -102,8 +113,15 @@ export const StateList: React.FC<StateListProps> = props => {
102113
useGlobalHandler<ClipboardEvent>(
103114
"paste",
104115
async e => {
116+
try{
117+
if((e.target as Element).localName == "vscode-text-field")
118+
return;
119+
}
120+
catch(exception){
121+
console.log("Not an element");
122+
console.log(exception);
123+
}
105124
e.preventDefault();
106-
107125
/// First we check png files copypasted wholesale from system - ie windows file explorer copy on a some.png file because navigator.clipboard.read() just panics in this case.
108126
/// We do it first because rejected navigator.clipboard.read also clears this list. Don't ask why.
109127
const data = e.clipboardData!;
@@ -169,9 +187,8 @@ export const StateList: React.FC<StateListProps> = props => {
169187
}
170188
}
171189
//We don't want to try to add png blobs since they always have less info than our direct data, they're just there for pasting into external editors
172-
if (found_valid_serialized_states) {
190+
if (found_valid_serialized_states)
173191
return;
174-
}
175192

176193
for (const item of items_with_possible_raw_pngs) {
177194
/// Next check if we have a png data blob
@@ -182,9 +199,8 @@ export const StateList: React.FC<StateListProps> = props => {
182199
// These are usually just pngs, but in theory (according to chromium docs, but this might be different in electron/vscode) these can also have metadata so we try to parse as dmi
183200
const dmi_or_png = await Dmi.parse(fileData);
184201
if (dmi_or_png.width == dmi.width && dmi_or_png.height == dmi.height) {
185-
for (const state of dmi_or_png.states) {
202+
for (const state of dmi_or_png.states)
186203
addState(state);
187-
}
188204
} else {
189205
// TODO: Just resize as needed
190206
messageHandler.sendEvent({
@@ -202,23 +218,43 @@ export const StateList: React.FC<StateListProps> = props => {
202218
);
203219

204220
const copyToClipboard = async (e: ClipboardEvent) => {
205-
if (selectedState != null) {
206-
e.preventDefault();
207-
const state = dmi.states[selectedState];
208-
const serializedState = JSON.stringify(state.serialize());
209-
const prefixedSerializedState = `${clipboardHeader}${serializedState}`;
210-
const imageBlob = await state.buildComposite();
211-
const textBlob = new Blob([prefixedSerializedState], { type: "text/plain" });
212-
const item = new ClipboardItem({ "image/png": imageBlob, "text/plain": textBlob });
213-
navigator.clipboard.write([item]);
214-
}
221+
if (selectedState == null)
222+
return;
223+
e.preventDefault();
224+
const state = dmi.states[selectedState];
225+
const serializedState = JSON.stringify(state.serialize());
226+
const prefixedSerializedState = `${clipboardHeader}${serializedState}`;
227+
const imageBlob = await state.buildComposite();
228+
const textBlob = new Blob([prefixedSerializedState], { type: "text/plain" });
229+
const item = new ClipboardItem({ "image/png": imageBlob, "text/plain": textBlob });
230+
navigator.clipboard.write([item]);
215231
};
216232

217-
useGlobalHandler<ClipboardEvent>("copy", e => copyToClipboard(e), [selectedState, dmi]);
233+
useGlobalHandler<ClipboardEvent>("copy", e => {
234+
if(renamingNode)
235+
return;
236+
try{
237+
if((e.target as Element).localName == "vscode-text-field")
238+
return;
239+
}
240+
catch(exception){
241+
console.log("Not an element");
242+
console.log(exception);
243+
}
244+
copyToClipboard(e);
245+
}, [selectedState, dmi, renamingNode]);
218246

219247
useGlobalHandler<ClipboardEvent>(
220248
"cut",
221249
async e => {
250+
try{
251+
if((e.target as Element).localName == "vscode-text-field")
252+
return;
253+
}
254+
catch(exception){
255+
console.log("Not an element");
256+
console.log(exception);
257+
}
222258
if (selectedState != null) {
223259
e.preventDefault();
224260
copyToClipboard(e);
@@ -233,12 +269,12 @@ export const StateList: React.FC<StateListProps> = props => {
233269
e => {
234270
switch (e.code) {
235271
case "Delete":
236-
if (selectedState !== null) {
237-
if (document.activeElement?.nodeName == "VSCODE-TEXT-FIELD")
238-
//move this to some context ?
239-
break;
240-
delete_state(selectedState)();
241-
}
272+
if (selectedState === null)
273+
break;
274+
if (document.activeElement?.nodeName == "VSCODE-TEXT-FIELD")
275+
//move this to some context ?
276+
break;
277+
delete_state(selectedState)();
242278
break;
243279
}
244280
},
@@ -257,6 +293,7 @@ export const StateList: React.FC<StateListProps> = props => {
257293
select={() => setSelectedState(index)}
258294
open={() => props.onOpen(state)}
259295
selected={selectedState == index}
296+
onEditingStateChanged={(value: boolean) => {setRenamingNode(value);}}
260297
duplicate={
261298
!!dmi.states.find(
262299
other_state =>

0 commit comments

Comments
 (0)