diff --git a/images/icons/v2/pen-medium-20.svg b/images/icons/v2/pen-medium-20.svg
index ab289df4c..e75ed1583 100644
--- a/images/icons/v2/pen-medium-20.svg
+++ b/images/icons/v2/pen-medium-20.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/images/icons/v2/pen-regular-20.svg b/images/icons/v2/pen-regular-20.svg
index e75ed1583..ab289df4c 100644
--- a/images/icons/v2/pen-regular-20.svg
+++ b/images/icons/v2/pen-regular-20.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/ts/components/MediaEditor.tsx b/ts/components/MediaEditor.tsx
index 0d874003c..8d18f387f 100644
--- a/ts/components/MediaEditor.tsx
+++ b/ts/components/MediaEditor.tsx
@@ -94,6 +94,12 @@ export const MediaEditor = ({
// Initial image load and Fabric canvas setup
useEffect(() => {
+ // This is important. We can't re-run this function if we've already setup
+ // a canvas since Fabric doesn't like that.
+ if (fabricCanvas) {
+ return;
+ }
+
const img = new Image();
img.onload = () => {
setImage(img);
@@ -117,7 +123,7 @@ export const MediaEditor = ({
img.onload = noop;
img.onerror = noop;
};
- }, [canvasId, imageSrc, onClose]);
+ }, [canvasId, fabricCanvas, imageSrc, onClose]);
const history = useFabricHistory(fabricCanvas);
@@ -142,7 +148,22 @@ export const MediaEditor = ({
ev => isCmdOrCtrl(ev) && ev.key === 't',
() => setEditMode(EditMode.Text),
],
- [ev => isCmdOrCtrl(ev) && ev.key === 'z', () => history?.undo()],
+ [
+ ev => isCmdOrCtrl(ev) && ev.key === 'z',
+ () => {
+ if (history?.canUndo()) {
+ history?.undo();
+ }
+ },
+ ],
+ [
+ ev => isCmdOrCtrl(ev) && ev.shiftKey && ev.key === 'z',
+ () => {
+ if (history?.canRedo()) {
+ history?.redo();
+ }
+ },
+ ],
[
ev => ev.key === 'Escape',
() => {
@@ -519,6 +540,7 @@ export const MediaEditor = ({
return;
}
+ obj.exitEditing();
obj.set(getTextStyleAttributes(textStyle, sliderValue));
fabricCanvas.requestRenderAll();
}, [editMode, fabricCanvas, sliderValue, textStyle]);
@@ -610,6 +632,8 @@ export const MediaEditor = ({
textStyle,
]);
+ const [isSaving, setIsSaving] = useState(false);
+
// In an ideal world we'd use to get the nice animation benefits
// but because of the way IText is implemented -- with a hidden textarea -- to
// capture keyboard events, we can't use ModalHost since that traps focus, and
@@ -963,12 +987,12 @@ export const MediaEditor = ({
'MediaEditor__control--selected': editMode === EditMode.Text,
})}
onClick={() => {
- if (!fabricCanvas) {
- return;
- }
-
if (editMode === EditMode.Text) {
setEditMode(undefined);
+ const obj = fabricCanvas?.getActiveObject();
+ if (obj instanceof MediaEditorFabricIText) {
+ obj.exitEditing();
+ }
} else {
setEditMode(EditMode.Text);
}
@@ -1069,12 +1093,37 @@ export const MediaEditor = ({
/>