Skip to content

Commit 0330822

Browse files
authored
Implement closing the current, and all, documents from the menu bar (#265)
Closes #261 Additional cleanup and refactoring with the way the backend relays the list of open documents to the frontend and prompts for confirmation.
1 parent 5b39776 commit 0330822

File tree

9 files changed

+148
-75
lines changed

9 files changed

+148
-75
lines changed

client/web/src/components/widgets/inputs/MenuBarInput.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ const menuEntries: MenuListEntries = [
8383
},
8484
],
8585
[
86-
{ label: "Close", shortcut: ["Ctrl", "W"] },
87-
{ label: "Close All", shortcut: ["Ctrl", "Alt", "W"] },
86+
{ label: "Close", shortcut: ["Ctrl", "W"], action: async () => (await wasm).close_active_document_with_confirmation() },
87+
{ label: "Close All", shortcut: ["Ctrl", "Alt", "W"], action: async () => (await wasm).close_all_documents_with_confirmation() },
8888
],
8989
[
9090
{ label: "Save", shortcut: ["Ctrl", "S"] },
@@ -154,6 +154,7 @@ export default defineComponent({
154154
window.open("https://www.graphite.design", "_blank");
155155
},
156156
actionNotImplemented() {
157+
// eslint-disable-next-line no-alert
157158
alert("This action is not yet implemented");
158159
},
159160
},

client/web/src/components/workspace/Panel.vue

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
:class="{ active: tabIndex === tabActiveIndex }"
88
v-for="(tabLabel, tabIndex) in tabLabels"
99
:key="tabLabel"
10-
@click.middle="closeTab(tabIndex)"
10+
@click.middle="handleTabClose(tabIndex)"
1111
@click="handleTabClick(tabIndex)"
1212
>
1313
<span>{{ tabLabel }}</span>
14-
<IconButton :icon="'CloseX'" :size="16" v-if="tabCloseButtons" @click.stop="closeTab(tabIndex)" />
14+
<IconButton :icon="'CloseX'" :size="16" v-if="tabCloseButtons" @click.stop="handleTabClose(tabIndex)" />
1515
</div>
1616
</div>
1717
<PopoverButton :icon="PopoverButtonIcon.VerticalEllipsis">
@@ -150,7 +150,7 @@ import Minimap from "../panels/Minimap.vue";
150150
import IconButton from "../widgets/buttons/IconButton.vue";
151151
import PopoverButton, { PopoverButtonIcon } from "../widgets/buttons/PopoverButton.vue";
152152
import { MenuDirection } from "../widgets/floating-menus/FloatingMenu.vue";
153-
import { ResponseType, registerResponseHandler, Response } from "../../utilities/response-handler";
153+
import { ResponseType, registerResponseHandler, Response, PromptConfirmationToCloseDocument } from "../../utilities/response-handler";
154154
155155
const wasm = import("../../../wasm/pkg");
156156
@@ -164,24 +164,37 @@ export default defineComponent({
164164
PopoverButton,
165165
},
166166
methods: {
167-
async handleTabClick(tabIndex: number) {
168-
if (this.panelType !== "Document") return;
169-
167+
handleTabClick(tabIndex: number) {
168+
if (this.panelType === "Document") this.selectDocument(tabIndex);
169+
},
170+
handleTabClose(tabIndex: number) {
171+
if (this.panelType === "Document") this.closeDocumentWithConfirmation(tabIndex);
172+
},
173+
async selectDocument(tabIndex: number) {
170174
const { select_document } = await wasm;
171175
select_document(tabIndex);
172176
},
173-
async closeTab(tabIndex: number) {
174-
if (this.panelType !== "Document") return;
175-
176-
const { close_document } = await wasm;
177+
async closeDocumentWithConfirmation(tabIndex: number) {
177178
// eslint-disable-next-line no-alert
178-
const result = window.confirm("Closing this document will permanently discard all work. Continue?");
179-
if (result) close_document(tabIndex);
179+
const userConfirmation = window.confirm("Closing this document will permanently discard all work. Continue?");
180+
if (userConfirmation) (await wasm).close_document(tabIndex);
181+
},
182+
async closeAllDocumentsWithConfirmation() {
183+
// eslint-disable-next-line no-alert
184+
const userConfirmation = window.confirm("Closing all documents will permanently discard all work in each of them. Continue?");
185+
if (userConfirmation) (await wasm).close_all_documents();
180186
},
181187
},
182188
mounted() {
183-
registerResponseHandler(ResponseType.PromptCloseConfirmationModal, (_responseData: Response) => {
184-
this.closeTab(this.tabActiveIndex);
189+
// TODO: Move these somewhere more appropriate to act upon all panels
190+
191+
registerResponseHandler(ResponseType.PromptConfirmationToCloseDocument, (responseData: Response) => {
192+
const promptData = responseData as PromptConfirmationToCloseDocument;
193+
this.closeDocumentWithConfirmation(promptData.document_index);
194+
});
195+
196+
registerResponseHandler(ResponseType.PromptConfirmationToCloseAllDocuments, (_responseData: Response) => {
197+
this.closeAllDocumentsWithConfirmation();
185198
});
186199
},
187200
props: {

client/web/src/components/workspace/Workspace.vue

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646

4747
<script lang="ts">
4848
import { defineComponent } from "vue";
49-
import { ResponseType, registerResponseHandler, Response, SetActiveDocument, NewDocument, CloseDocument } from "../../utilities/response-handler";
49+
import { ResponseType, registerResponseHandler, Response, SetActiveDocument, UpdateOpenDocumentsList } from "../../utilities/response-handler";
5050
import LayoutRow from "../layout/LayoutRow.vue";
5151
import LayoutCol from "../layout/LayoutCol.vue";
5252
import Panel from "./Panel.vue";
@@ -59,15 +59,10 @@ export default defineComponent({
5959
},
6060
6161
mounted() {
62-
registerResponseHandler(ResponseType.NewDocument, (responseData: Response) => {
63-
const documentData = responseData as NewDocument;
64-
if (documentData) this.documents.push(documentData.document_name);
65-
});
66-
67-
registerResponseHandler(ResponseType.CloseDocument, (responseData: Response) => {
68-
const documentData = responseData as CloseDocument;
69-
if (documentData) {
70-
this.documents.splice(documentData.document_index, 1);
62+
registerResponseHandler(ResponseType.UpdateOpenDocumentsList, (responseData: Response) => {
63+
const documentListData = responseData as UpdateOpenDocumentsList;
64+
if (documentListData) {
65+
this.documents = documentListData.open_documents;
7166
}
7267
});
7368
@@ -80,7 +75,7 @@ export default defineComponent({
8075
data() {
8176
return {
8277
activeDocument: 0,
83-
documents: ["Untitled Document"],
78+
documents: ["Untitled Document"], // TODO: start as an empty list
8479
};
8580
},
8681
});

client/web/src/utilities/response-handler.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ export enum ResponseType {
1818
CollapseFolder = "CollapseFolder",
1919
SetActiveTool = "SetActiveTool",
2020
SetActiveDocument = "SetActiveDocument",
21-
NewDocument = "NewDocument",
22-
CloseDocument = "CloseDocument",
21+
UpdateOpenDocumentsList = "UpdateOpenDocumentsList",
2322
UpdateWorkingColors = "UpdateWorkingColors",
24-
PromptCloseConfirmationModal = "PromptCloseConfirmationModal",
2523
SetCanvasZoom = "SetCanvasZoom",
2624
SetRotation = "SetRotation",
25+
PromptConfirmationToCloseDocument = "PromptConfirmationToCloseDocument",
26+
PromptConfirmationToCloseAllDocuments = "PromptConfirmationToCloseAllDocuments",
2727
}
2828

2929
export function registerResponseHandler(responseType: ResponseType, callback: ResponseCallback) {
@@ -57,10 +57,8 @@ function parseResponse(responseType: string, data: any): Response {
5757
return newSetActiveTool(data.SetActiveTool);
5858
case "SetActiveDocument":
5959
return newSetActiveDocument(data.SetActiveDocument);
60-
case "NewDocument":
61-
return newNewDocument(data.NewDocument);
62-
case "CloseDocument":
63-
return newCloseDocument(data.CloseDocument);
60+
case "UpdateOpenDocumentsList":
61+
return newUpdateOpenDocumentsList(data.UpdateOpenDocumentsList);
6462
case "UpdateCanvas":
6563
return newUpdateCanvas(data.UpdateCanvas);
6664
case "SetCanvasZoom":
@@ -71,20 +69,22 @@ function parseResponse(responseType: string, data: any): Response {
7169
return newExportDocument(data.ExportDocument);
7270
case "UpdateWorkingColors":
7371
return newUpdateWorkingColors(data.UpdateWorkingColors);
74-
case "PromptCloseConfirmationModal":
75-
return {};
72+
case "PromptConfirmationToCloseDocument":
73+
return newPromptConfirmationToCloseDocument(data.PromptConfirmationToCloseDocument);
74+
case "PromptConfirmationToCloseAllDocuments":
75+
return newPromptConfirmationToCloseAllDocuments(data.PromptConfirmationToCloseAllDocuments);
7676
default:
7777
throw new Error(`Unrecognized origin/responseType pair: ${origin}, '${responseType}'`);
7878
}
7979
}
8080

8181
export type Response = SetActiveTool | UpdateCanvas | DocumentChanged | CollapseFolder | ExpandFolder | UpdateWorkingColors | SetCanvasZoom | SetRotation;
8282

83-
export interface CloseDocument {
84-
document_index: number;
83+
export interface UpdateOpenDocumentsList {
84+
open_documents: Array<string>;
8585
}
86-
function newCloseDocument(input: any): CloseDocument {
87-
return { document_index: input.document_index };
86+
function newUpdateOpenDocumentsList(input: any): UpdateOpenDocumentsList {
87+
return { open_documents: input.open_documents };
8888
}
8989

9090
export interface Color {
@@ -127,15 +127,19 @@ function newSetActiveDocument(input: any): SetActiveDocument {
127127
};
128128
}
129129

130-
export interface NewDocument {
131-
document_name: string;
130+
export interface PromptConfirmationToCloseDocument {
131+
document_index: number;
132132
}
133-
function newNewDocument(input: any): NewDocument {
133+
function newPromptConfirmationToCloseDocument(input: any): PromptConfirmationToCloseDocument {
134134
return {
135-
document_name: input.document_name,
135+
document_index: input.document_index,
136136
};
137137
}
138138

139+
function newPromptConfirmationToCloseAllDocuments(_input: any): {} {
140+
return {};
141+
}
142+
139143
export interface UpdateCanvas {
140144
document: string;
141145
}

client/web/wasm/src/document.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,29 @@ pub fn select_document(document: usize) -> Result<(), JsValue> {
2828
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::SelectDocument(document)).map_err(convert_error))
2929
}
3030

31+
#[wasm_bindgen]
32+
pub fn new_document() -> Result<(), JsValue> {
33+
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::NewDocument).map_err(convert_error))
34+
}
35+
3136
#[wasm_bindgen]
3237
pub fn close_document(document: usize) -> Result<(), JsValue> {
3338
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::CloseDocument(document)).map_err(convert_error))
3439
}
3540

3641
#[wasm_bindgen]
37-
pub fn new_document() -> Result<(), JsValue> {
38-
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::NewDocument).map_err(convert_error))
42+
pub fn close_all_documents() -> Result<(), JsValue> {
43+
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::CloseAllDocuments).map_err(convert_error))
44+
}
45+
46+
#[wasm_bindgen]
47+
pub fn close_active_document_with_confirmation() -> Result<(), JsValue> {
48+
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::CloseActiveDocumentWithConfirmation).map_err(convert_error))
49+
}
50+
51+
#[wasm_bindgen]
52+
pub fn close_all_documents_with_confirmation() -> Result<(), JsValue> {
53+
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::CloseAllDocumentsWithConfirmation).map_err(convert_error))
3954
}
4055

4156
// TODO: Call event when the panels are resized
@@ -196,23 +211,23 @@ pub fn toggle_layer_expansion(path: Vec<LayerId>) -> Result<(), JsValue> {
196211
.map_err(convert_error)
197212
}
198213

199-
/// Renames a layer from the layer list
214+
/// Renames a layer from the layer list
200215
#[wasm_bindgen]
201216
pub fn rename_layer(path: Vec<LayerId>, new_name: String) -> Result<(), JsValue> {
202217
EDITOR_STATE
203218
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::RenameLayer(path, new_name)))
204219
.map_err(convert_error)
205220
}
206221

207-
/// Deletes a layer from the layer list
222+
/// Deletes a layer from the layer list
208223
#[wasm_bindgen]
209224
pub fn delete_layer(path: Vec<LayerId>) -> Result<(), JsValue> {
210225
EDITOR_STATE
211226
.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::DeleteLayer(path)))
212227
.map_err(convert_error)
213228
}
214229

215-
/// Requests the backend to add a layer to the layer list
230+
/// Requests the backend to add a layer to the layer list
216231
#[wasm_bindgen]
217232
pub fn add_folder(path: Vec<LayerId>) -> Result<(), JsValue> {
218233
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(DocumentMessage::AddFolder(path))).map_err(convert_error)

core/editor/src/document/document_file.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
use crate::{consts::ROTATE_SNAP_INTERVAL, frontend::layer_panel::*, EditorError};
2-
use document_core::{document::Document as InteralDocument, layers::Layer, LayerId};
2+
use document_core::{document::Document as InternalDocument, layers::Layer, LayerId};
33
use glam::{DAffine2, DVec2};
44
use serde::{Deserialize, Serialize};
55
use std::collections::HashMap;
66

77
#[derive(Clone, Debug)]
88
pub struct Document {
9-
pub document: InteralDocument,
9+
pub document: InternalDocument,
1010
pub name: String,
1111
pub layer_data: HashMap<Vec<LayerId>, LayerData>,
1212
}
1313

1414
impl Default for Document {
1515
fn default() -> Self {
1616
Self {
17-
document: InteralDocument::default(),
17+
document: InternalDocument::default(),
1818
name: String::from("Untitled Document"),
1919
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
2020
}
@@ -24,7 +24,7 @@ impl Default for Document {
2424
impl Document {
2525
pub fn with_name(name: String) -> Self {
2626
Self {
27-
document: InteralDocument::default(),
27+
document: InternalDocument::default(),
2828
name,
2929
layer_data: vec![(vec![], LayerData::new(true))].into_iter().collect(),
3030
}

0 commit comments

Comments
 (0)