runlab 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app/app.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createEditor } from "./core/editor.js";
2
2
  import { createNavbar } from "./core/navbar.js";
3
3
  import { FileExplorer } from "./fileExplorer/FileExplorer.js";
4
+ import { createTerminal } from "./terminal/terminal.js";
4
5
 
5
6
  export function generateContainer(parentId,w,h) {
6
7
  const container = document.createElement("div");
@@ -13,13 +14,50 @@ export function generateContainer(parentId,w,h) {
13
14
  justifyContent: "center",
14
15
  alignItems: "center",
15
16
  overflow: "hidden"
16
- });
17
+ });
17
18
  document.getElementById(parentId).appendChild(container);
18
19
  gridTemplate("runlab-container", w, h);
19
- createNavbar("runlab-navbar");
20
20
  createEditor("runlab-editor","");
21
21
 
22
- new FileExplorer("runlab-file-explorer");
22
+ // View and Editor toggle
23
+ const editor = document.getElementById("runlab-editor");
24
+ const overlay = document.createElement("div");
25
+ overlay.id = "runlab-view";
26
+ Object.assign(overlay.style, {
27
+ position: "absolute",
28
+ inset: "0",
29
+ backgroundColor: "#303030"
30
+ });
31
+ editor.style.position = "relative";
32
+ editor.appendChild(overlay);
33
+
34
+ const viewContent = document.createElement("iframe");
35
+ viewContent.id = "runlab-view-content";
36
+ Object.assign(viewContent.style, {
37
+ width: "100%",
38
+ height: "100%",
39
+ border: "none",
40
+ backgroundColor: "rgba(255, 255, 255, 0.8)"
41
+ });
42
+ document.getElementById("runlab-view").appendChild(viewContent);
43
+
44
+ const fe = new FileExplorer("runlab-file-explorer");
45
+ const root = fe.getRoot();
46
+ setFileExplorerNode(fe);
47
+ createNavbar("runlab-navbar");
48
+
49
+ setEditorActive(false); // Start with editor off
50
+
51
+ // ===============================
52
+
53
+ createTerminal("runlab-terminal",root);
54
+
55
+ }
56
+
57
+ export function appendViewContent(codeReturned) {
58
+ const viewContent = document.getElementById("runlab-view-content");
59
+ viewContent.srcdoc = codeReturned;
60
+
23
61
  }
24
62
 
25
63
  function gridTemplate(parent) {
@@ -60,7 +98,10 @@ function gridTemplate(parent) {
60
98
  Object.assign(editor.style, {
61
99
  gridColumn: "2 / 3",
62
100
  gridRow: "2 / 3", // linha logo após a navbar
63
- border: "1px solid #09090b"
101
+ border: "1px solid #09090b",
102
+ width: "100%",
103
+ height: "100%",
104
+ overflow: "hidden"
64
105
  });
65
106
 
66
107
  // Right Bottom
@@ -69,6 +110,9 @@ function gridTemplate(parent) {
69
110
  Object.assign(terminal.style, {
70
111
  gridColumn: "2 / 3",
71
112
  gridRow: "3 / 4", // última linha
113
+ width: "100%",
114
+ height: "100%",
115
+ overflow: "hidden",
72
116
  border: "1px solid #09090b"
73
117
  });
74
118
 
@@ -77,4 +121,136 @@ function gridTemplate(parent) {
77
121
  grid.appendChild(editor);
78
122
  grid.appendChild(terminal);
79
123
  document.getElementById(parent).appendChild(grid);
124
+ }
125
+
126
+ export function setViewActive(status = false){
127
+
128
+ const editorBtn = document.getElementById("runlab-editor-btn");
129
+ const viewBtn = document.getElementById("runlab-view-btn");
130
+
131
+ editorBtn.classList.remove("active");
132
+ viewBtn.classList.remove("active");
133
+ if(status){
134
+ viewBtn.classList.add("active");
135
+ }
136
+ if(!status){
137
+ editorBtn.classList.add("active");
138
+ }
139
+
140
+ [editorBtn, viewBtn].forEach(b => {
141
+ b.style.background = "transparent";
142
+ b.style.color = "#000";
143
+ });
144
+
145
+ if(status){
146
+ document.getElementById("runlab-view").style.display = "block";
147
+ viewBtn.style.background = "#303030";
148
+ viewBtn.style.color = "#fff";
149
+ }
150
+
151
+ if (!status){
152
+ document.getElementById("runlab-view").style.display = "none";
153
+ editorBtn.style.background = "#303030";
154
+ editorBtn.style.color = "#fff";
155
+ }
156
+ }
157
+
158
+ export function setEditorActive(status = true){
159
+ const editorWarning = document.createElement("div");
160
+ editorWarning.id = "runlab-editor-warning";
161
+ Object.assign(editorWarning.style, {
162
+ position: "absolute",
163
+ inset: "0",
164
+ display: "flex",
165
+ flexDirection: "column",
166
+ justifyContent: "center",
167
+ alignItems: "center",
168
+ gap: "8px",
169
+ textAlign: "center",
170
+ });
171
+
172
+ //================================ SVG Warning Icon ================================
173
+ const warningImage = document.createElementNS("http://www.w3.org/2000/svg", "svg");
174
+
175
+ warningImage.setAttribute("viewBox", "0 0 128 128");
176
+ warningImage.setAttribute("width", "128");
177
+ warningImage.setAttribute("height", "128");
178
+ warningImage.setAttribute("fill", "none");
179
+
180
+ warningImage.innerHTML = `
181
+ <path
182
+ d="M24 8 H80 L104 32 V112 H24 Z M80 8 V32 H104"
183
+ stroke="currentColor"
184
+ stroke-width="4"
185
+ stroke-linejoin="round"
186
+ />
187
+
188
+ <rect x="40" y="44" rx="6" ry="6" width="20" height="4"
189
+ fill="currentColor" transform="rotate(-25 36 44)" />
190
+ <rect x="68" y="44" rx="6" ry="6" width="20" height="4"
191
+ fill="currentColor" transform="rotate(25 64 44)" />
192
+
193
+ <circle cx="50" cy="64" r="5" fill="currentColor" />
194
+ <circle cx="78" cy="64" r="5" fill="currentColor" />
195
+
196
+ <rect x="44" y="80" rx="6" ry="6" width="40" height="5"
197
+ fill="currentColor" />
198
+ `;
199
+
200
+ warningImage.style.color = "#aaa";
201
+
202
+ //================================ SVG Warning Text ================================
203
+
204
+ const warningText = document.createElement("p");
205
+ warningText.textContent = "No file selected.";
206
+ warningText.style.color = "#aaa";
207
+ warningText.style.fontSize = "0.9em";
208
+ warningText.style.margin = "0";
209
+
210
+ const warningText2 = document.createElement("p");
211
+ warningText2.textContent =
212
+ "Please create a file using right click on the file explorer at the left container and select it.";
213
+ warningText2.style.color = "#aaa";
214
+ warningText2.style.fontSize = "0.75em";
215
+ warningText2.style.margin = "0";
216
+
217
+ //====================================================================================
218
+
219
+ editorWarning.appendChild(warningImage);
220
+ editorWarning.appendChild(warningText);
221
+ editorWarning.appendChild(warningText2);
222
+
223
+ const editorCodeMirror = document.getElementsByClassName("cm-editor ͼ1 ͼ3 ͼ16")[0];
224
+
225
+ if(!status){
226
+ document.getElementById("runlab-editor").appendChild(editorWarning);
227
+ if(editorCodeMirror){
228
+ editorCodeMirror.display = "none";
229
+ }
230
+ }
231
+
232
+ if(status){
233
+ const existingWarning = document.getElementById("runlab-editor-warning");
234
+ if(existingWarning){
235
+ existingWarning.remove();
236
+ }
237
+ if(editorCodeMirror){
238
+ editorCodeMirror.display = "block";
239
+ }
240
+ }
241
+
242
+ }
243
+
244
+ let feNode = null;
245
+
246
+ function setFileExplorerNode(node) {
247
+ feNode = node;
248
+ }
249
+
250
+ export function getFeExecutables() {
251
+ return feNode.getExecutables()
252
+ }
253
+
254
+ export function getFileNode(file) {
255
+ return feNode.getFileNode(file);
80
256
  }
@@ -11,7 +11,6 @@ import { json } from "@codemirror/lang-json";
11
11
  import { markdown } from "@codemirror/lang-markdown";
12
12
  import { xml } from "@codemirror/lang-xml";
13
13
  import { yaml } from "@codemirror/lang-yaml";
14
- import { go } from "@codemirror/lang-go";
15
14
 
16
15
  // VS Code look & feel
17
16
  import { vscodeDark } from "@uiw/codemirror-theme-vscode";
@@ -29,12 +28,26 @@ export const languageCompartment = new Compartment();
29
28
 
30
29
  export function setActiveFile(file) {
31
30
  activeFile = file;
31
+ let navbarFileName = document.getElementById("runlab-navbar-active-file-name")
32
+ if (navbarFileName) {
33
+ navbarFileName.textContent = file ? `${file.name}.${file.ext}` : "";
34
+ }
32
35
  }
33
36
 
34
37
  function getActiveFile() {
35
38
  return activeFile;
36
39
  }
37
40
 
41
+ export function getCurrentCode() {
42
+ const activeFile = getActiveFile();
43
+ return activeFile ? activeFile.content : "";
44
+ }
45
+
46
+ export function getCurrentExtension() {
47
+ const activeFile = getActiveFile();
48
+ return activeFile ? activeFile.ext : "";
49
+ }
50
+
38
51
  export function languageFromExtension(ext) {
39
52
  switch (ext) {
40
53
  case "txt": return [];
@@ -48,12 +61,11 @@ export function languageFromExtension(ext) {
48
61
  case "css":return css();
49
62
  case "js":return javascript({ typescript: false });
50
63
  case "ts":return javascript({ typescript: true });
51
- case "go":return go();
64
+ case "lua":return [];
52
65
  default:return [];
53
66
  }
54
67
  }
55
68
 
56
-
57
69
  export function createEditor(parentId, initialCode = "") {
58
70
  const parent = document.getElementById(parentId);
59
71
  if (!parent) throw new Error(`Elemento #${parentId} não encontrado`);
@@ -1,4 +1,7 @@
1
-
1
+ import { sendCode } from "../../index.js";
2
+ import { setViewActive } from "../app.js";
3
+ import { getCurrentCode, getCurrentExtension } from "./editor.js";
4
+ import Swal from "sweetalert2";
2
5
 
3
6
  export function createNavbar(parentId) {
4
7
  const navbar = document.createElement("nav");
@@ -8,28 +11,144 @@ export function createNavbar(parentId) {
8
11
  height: "100%"
9
12
  });
10
13
 
14
+ // ========== Menu Items ==========
15
+
11
16
  const ul = document.createElement("ul");
12
17
  Object.assign(ul.style, {
13
18
  listStyleType: "none",
14
19
  margin: "0",
15
20
  padding: "0",
16
- display: "flex"
21
+ display: "flex",
22
+ alignItems: "center",
23
+ width: "100%"
17
24
  });
18
25
  navbar.appendChild(ul);
19
26
 
20
- const menuItems = ["File", "Edit", "View", "Help"];
27
+
28
+ // ========== Left Menu Items ==========
29
+
30
+ const leftGroup = document.createElement("div");
31
+ Object.assign(leftGroup.style, {
32
+ display: "flex",
33
+ alignItems: "center"
34
+ });
35
+ ul.appendChild(leftGroup);
36
+
37
+ const menuItems = ["Run", "Help"];
21
38
  menuItems.forEach(item => {
22
- const li = document.createElement("li");
39
+ const li = document.createElement("li");
23
40
  li.textContent = item;
24
- ul.appendChild(li);
41
+
25
42
  Object.assign(li.style, {
26
- marginRight: "20px",
43
+ marginRight: "15px",
44
+ marginLeft: "15px",
27
45
  padding: "10px 14px",
28
46
  cursor: "pointer",
29
- color: "#fff",
30
- textDecoration: "none"
47
+ color: "#fff"
31
48
  });
49
+ if (item === "Run") {
50
+ li.addEventListener("click", () => {
51
+ sendCode(getCurrentCode(),getCurrentExtension());
52
+ setViewActive(true);
53
+ });
54
+ }
55
+ if (item === "Help") {
56
+ li.addEventListener("click", () => {
57
+ Swal.fire({
58
+ title: "RunLab Help",
59
+ html: `
60
+ <p>RunLab is a web-based code editor and runtime environment.</p>
61
+ <p>Here's how to use it:</p>
62
+ <ul style="text-align: left;">
63
+ <li><strong>File Explorer:</strong> Use the file explorer to create, open, and manage your project files. Start it by right clicking on the left container.</li>
64
+ <li><strong>Editor:</strong> Write your code in the editor pane. It supports multiple file types. To use it you should click on a file.</li>
65
+ <li><strong>View:</strong> After writing your code, click the "Run" button in the navbar to execute it. The output will be displayed in the view pane.</li>
66
+ <li><strong>Terminal:</strong> You can run terminal commands in the terminal pane.</li>
67
+ </ul>
68
+ <p>Available terminal commands:</p>
69
+ <ul style="text-align: left;">
70
+ <li><span style="font-weight: bold;">ls</span>: List directory contents</li>
71
+ <li><span style="font-weight: bold;">cd</span>: Change directory</li>
72
+ <li><span style="font-weight: bold;">clear</span>: Clear the terminal</li>
73
+ <li><span style="font-weight: bold;">pwd</span>: Print current directory</li>
74
+ <li><span style="font-weight: bold;">run</span>: Run a file (e.g., run script.js)</li>
75
+ <li><span style="font-weight: bold;">help | h</span>: Show help message</li>
76
+ </ul>
77
+ `,
78
+ width: 700,
79
+ confirmButtonText: "Got it!"
80
+ })
81
+ });
82
+ }
83
+
84
+ leftGroup.appendChild(li);
32
85
  });
33
86
 
87
+ // ========== Append navbar ==========
88
+
34
89
  document.getElementById(parentId).appendChild(navbar);
90
+
91
+ // ========== ActiveFile Name ==========
92
+ const activeFileName = document.createElement("li");
93
+ activeFileName.id = "runlab-navbar-active-file-name";
94
+
95
+ Object.assign(activeFileName.style, {
96
+ margin: "0 auto",
97
+ padding: "10px 14px",
98
+ fontWeight: "600",
99
+ color: "#FFDE21",
100
+ whiteSpace: "nowrap"
101
+ });
102
+
103
+ ul.appendChild(activeFileName);
104
+ // ========== Editor/View Toggle ==========
105
+
106
+ const toggleLi = document.createElement("li");
107
+ Object.assign(toggleLi.style, {
108
+
109
+ padding: "4px 10px"
110
+ });
111
+
112
+ const toggle = document.createElement("div");
113
+ Object.assign(toggle.style, {
114
+ display: "grid",
115
+ gridTemplateColumns: "1fr 1fr",
116
+ width: "180px",
117
+ height: "28px",
118
+ border: "2px solid #09090b",
119
+ borderRadius: "8px",
120
+ overflow: "hidden",
121
+ background: "#e5e7eb",
122
+ fontFamily: "sans-serif",
123
+ marginLeft: "20px"
124
+ });
125
+
126
+ const editorBtn = document.createElement("button");
127
+ editorBtn.id = "runlab-editor-btn";
128
+ editorBtn.textContent = "Editor";
129
+ editorBtn.classList.add("active");
130
+
131
+ const viewBtn = document.createElement("button");
132
+ viewBtn.id = "runlab-view-btn";
133
+ viewBtn.textContent = "View";
134
+
135
+ [editorBtn, viewBtn].forEach(btn => {
136
+ Object.assign(btn.style, {
137
+ border: "none",
138
+ cursor: "pointer",
139
+ fontWeight: "500",
140
+ transition: "0.2s",
141
+ color: "#000"
142
+ });
143
+ });
144
+
145
+ toggle.append(editorBtn, viewBtn);
146
+ toggleLi.appendChild(toggle);
147
+ ul.appendChild(toggleLi);
148
+
149
+ //Initialize with editor active
150
+ setViewActive(false);
151
+
152
+ editorBtn.onclick = () => setViewActive(false);
153
+ viewBtn.onclick = () => setViewActive(true);
35
154
  }
@@ -2,15 +2,46 @@ import { Folder } from "./Folder.js";
2
2
  import { File } from "./File.js";
3
3
  import { updateEditorContentById, setActiveFile } from "../core/editor.js";
4
4
  import Swal from "sweetalert2";
5
+ import { setEditorActive, setViewActive } from "../app.js";
5
6
 
6
7
  export class FileExplorer {
7
8
  constructor(parentId) {
8
9
  this.container = document.getElementById(parentId);
9
10
  this.root = new Folder("root", null);
10
-
11
+ this.execFiles = [];
12
+ this.filesPath = [];
13
+ this.path = [];
11
14
  this.render();
12
15
  }
13
16
 
17
+ getRoot() {
18
+ return this.root;
19
+ }
20
+
21
+ getFileNode(file) {
22
+ const name = file.name.split(".")[0];
23
+ const ext = file.name.split(".")[1];
24
+ const path = file.path;
25
+ let currentNode = this.root;
26
+
27
+ for (let i = 1; i < path.length; i++) {
28
+ const part = path[i];
29
+ const nextNode = currentNode.children.find(c => c.name === part && !c.ext);
30
+ if (!nextNode) return null;
31
+ currentNode = nextNode;
32
+ }
33
+
34
+ return currentNode.children.find(c => c.name === name && c.ext === ext);
35
+ }
36
+
37
+ getExecutables() {
38
+ return this.execFiles;
39
+ }
40
+
41
+ setExecutables(files) {
42
+ this.execFiles = files;
43
+ }
44
+
14
45
  /* =========================
15
46
  RENDER
16
47
  ========================== */
@@ -50,12 +81,17 @@ export class FileExplorer {
50
81
  btn.style.paddingBottom = "8px";
51
82
  btn.style.cursor = "pointer";
52
83
 
84
+
53
85
 
54
86
  btn.onmouseover = () => btn.style.backgroundColor = "#555";
55
87
  btn.onmouseout = () => btn.style.backgroundColor = "transparent";
56
88
 
57
89
  const ul = document.createElement("ul");
58
90
  ul.style.display = "none";
91
+ ul.style.listStyle = "none";
92
+ ul.style.margin = "0";
93
+ ul.style.padding = "0 0 0 24px";
94
+
59
95
 
60
96
  btn.onclick = () => this.toggle(ul);
61
97
  btn.oncontextmenu = e => {
@@ -79,9 +115,12 @@ export class FileExplorer {
79
115
  li.style.paddingTop = "8px";
80
116
  li.style.paddingBottom = "8px";
81
117
  li.style.cursor = "pointer";
118
+
82
119
 
83
120
  li.onclick = () => {
84
121
  setActiveFile(node);
122
+ setViewActive(false);
123
+ setEditorActive(true);
85
124
  updateEditorContentById("runlab-editor", node.content);
86
125
  };
87
126
 
@@ -92,8 +131,12 @@ export class FileExplorer {
92
131
  };
93
132
 
94
133
  parentUl.appendChild(li);
134
+ this.filesPath.push({ name: `${node.name}.${node.ext}`, path: node.parent.path });
135
+
95
136
  }
96
137
  });
138
+
139
+ this.setExecutables(this.filesPath);
97
140
  }
98
141
 
99
142
  toggle(el) {
@@ -138,6 +181,7 @@ export class FileExplorer {
138
181
  input: 'text',
139
182
  showCancelButton: true,
140
183
  confirmButtonText: 'Create',
184
+ draggable: true,
141
185
  preConfirm: (name) => {
142
186
  if (name) {
143
187
  this.addFolder(this.root, name);
@@ -151,20 +195,21 @@ export class FileExplorer {
151
195
  title: 'Choose extension and filename',
152
196
  html:
153
197
  '<select id="swal-select" class="swal2-input">' +
154
- '<option value="txt">.txt - Text</option>' +
155
- '<option value="md">.md - Text</option>' +
156
- '<option value="json">.json - Data</option>' +
157
- '<option value="yaml">.yaml - Data</option>' +
158
- '<option value="toml">.toml - Data</option>' +
159
- '<option value="html">.html - Markup</option>' +
160
- '<option value="xml">.xml - Markup</option>' +
161
- '<option value="css">.css - Style</option>' +
162
- '<option value="js">.js - JavaScript</option>' +
163
- '<option value="ts">.ts - TypeScript</option>' +
164
- '<option value="go">.go - GO</option>' +
198
+ '<option value="txt">📄 .txt - Text</option>' +
199
+ '<option value="md">📝 .md - Text</option>' +
200
+ '<option value="json">🧩 .json - Data</option>' +
201
+ '<option value="yaml">🧩 .yaml - Data</option>' +
202
+ '<option value="toml">🧩 .toml - Data</option>' +
203
+ '<option value="html">🌐 .html - Markup</option>' +
204
+ '<option value="xml">🌐 .xml - Markup</option>' +
205
+ '<option value="css">🎨 .css - Style</option>' +
206
+ '<option value="js">🟨 .js - JavaScript</option>' +
207
+ '<option value="ts">🟦 .ts - TypeScript</option>' +
208
+ '<option value="py">🐍 .py - Python</option>' +
165
209
  '</select>' +
166
210
  '<input id="swal-input" class="swal2-input" placeholder="filename">',
167
211
  focusConfirm: false,
212
+ draggable: true,
168
213
  preConfirm: () => {
169
214
  return [
170
215
  document.getElementById('swal-select').value,
@@ -177,10 +222,21 @@ export class FileExplorer {
177
222
  this.addFile(this.root, text, selection);
178
223
  }
179
224
  })
180
- }
225
+ }
181
226
  ]);
182
227
 
183
228
  this.openMenu(e, menu);
229
+
230
+ const closeMenu = () => {
231
+ menu.remove();
232
+ document.removeEventListener("click", closeMenu);
233
+ document.removeEventListener("contextmenu", closeMenu);
234
+ };
235
+
236
+ setTimeout(() => {
237
+ document.addEventListener("click", closeMenu);
238
+ document.addEventListener("contextmenu", closeMenu);
239
+ });
184
240
  }
185
241
 
186
242
  showFolderMenu(e, folder) {
@@ -190,6 +246,7 @@ export class FileExplorer {
190
246
  input: 'text',
191
247
  showCancelButton: true,
192
248
  confirmButtonText: 'Create',
249
+ draggable: true,
193
250
  preConfirm: (name) => {
194
251
  if (name) {
195
252
  this.addFolder(folder, name);
@@ -200,20 +257,21 @@ export class FileExplorer {
200
257
  title: 'Choose extension and filename',
201
258
  html:
202
259
  '<select id="swal-select" class="swal2-input">' +
203
- '<option value="txt">.txt - Text</option>' +
204
- '<option value="md">.md - Text</option>' +
205
- '<option value="json">.json - Data</option>' +
206
- '<option value="yaml">.yaml - Data</option>' +
207
- '<option value="toml">.toml - Data</option>' +
208
- '<option value="html">.html - Markup</option>' +
209
- '<option value="xml">.xml - Markup</option>' +
210
- '<option value="css">.css - Style</option>' +
211
- '<option value="js">.js - JavaScript</option>' +
212
- '<option value="ts">.ts - TypeScript</option>' +
213
- '<option value="go">.go - GO</option>' +
260
+ '<option value="txt">📄 .txt - Text</option>' +
261
+ '<option value="md">📝 .md - Text</option>' +
262
+ '<option value="json">🧩 .json - Data</option>' +
263
+ '<option value="yaml">🧩 .yaml - Data</option>' +
264
+ '<option value="toml">🧩 .toml - Data</option>' +
265
+ '<option value="html">🌐 .html - Markup</option>' +
266
+ '<option value="xml">🌐 .xml - Markup</option>' +
267
+ '<option value="css">🎨 .css - Style</option>' +
268
+ '<option value="js">🟨 .js - JavaScript</option>' +
269
+ '<option value="ts">🟦 .ts - TypeScript</option>' +
270
+ '<option value="py">🐍 .py - Python</option>' +
214
271
  '</select>' +
215
272
  '<input id="swal-input" class="swal2-input" placeholder="filename">',
216
273
  focusConfirm: false,
274
+ draggable: true,
217
275
  preConfirm: () => {
218
276
  return [
219
277
  document.getElementById('swal-select').value,
@@ -223,7 +281,7 @@ export class FileExplorer {
223
281
  }).then((result) => {
224
282
  if (result.value) {
225
283
  const [selection, text] = result.value;
226
- this.addFile(this.root, text, selection);
284
+ this.addFile(folder, text, selection);
227
285
  }
228
286
  })
229
287
  },
@@ -232,6 +290,7 @@ export class FileExplorer {
232
290
  input: 'text',
233
291
  showCancelButton: true,
234
292
  confirmButtonText: 'Rename',
293
+ draggable: true,
235
294
  preConfirm: (name) => {
236
295
  if (name) {
237
296
  this.rename(folder, name);
@@ -242,6 +301,17 @@ export class FileExplorer {
242
301
  ]);
243
302
 
244
303
  this.openMenu(e, menu);
304
+
305
+ const closeMenu = () => {
306
+ menu.remove();
307
+ document.removeEventListener("click", closeMenu);
308
+ document.removeEventListener("contextmenu", closeMenu);
309
+ };
310
+
311
+ setTimeout(() => {
312
+ document.addEventListener("click", closeMenu);
313
+ document.addEventListener("contextmenu", closeMenu);
314
+ });
245
315
  }
246
316
 
247
317
  showFileMenu(e, file) {
@@ -251,6 +321,7 @@ export class FileExplorer {
251
321
  input: 'text',
252
322
  showCancelButton: true,
253
323
  confirmButtonText: 'Rename',
324
+ draggable: true,
254
325
  preConfirm: (name) => {
255
326
  if (name) {
256
327
  this.rename(file, name);
@@ -261,6 +332,7 @@ export class FileExplorer {
261
332
  title: '',
262
333
  showCancelButton: true,
263
334
  confirmButtonText: 'Delete',
335
+ draggable: true,
264
336
  preConfirm: (name) => {
265
337
  if (name) {
266
338
  this.delete(file);
@@ -270,6 +342,17 @@ export class FileExplorer {
270
342
  ]);
271
343
 
272
344
  this.openMenu(e, menu);
345
+
346
+ const closeMenu = () => {
347
+ menu.remove();
348
+ document.removeEventListener("click", closeMenu);
349
+ document.removeEventListener("contextmenu", closeMenu);
350
+ };
351
+
352
+ setTimeout(() => {
353
+ document.addEventListener("click", closeMenu);
354
+ document.addEventListener("contextmenu", closeMenu);
355
+ });
273
356
  }
274
357
 
275
358
  /* =========================