vzcode 0.5.0 → 0.6.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.
@@ -0,0 +1 @@
1
+ .vz-sidebar{height:100%;width:200px;background:rgb(54,65,89);display:flex;flex-direction:column;justify-content:space-between}.vz-sidebar .full-box{padding:7px;display:flex;flex-direction:row;align-items:center;justify-content:space-between}.vz-sidebar .files{overflow:auto}.vz-sidebar .file-or-directory{display:flex;flex-direction:row;align-items:center;justify-content:space-between;cursor:pointer;margin:0 10px;color:#dbdde1;border-radius:5px}.vz-sidebar .link-name{margin-top:30px;margin-left:10px;color:#9aa0ac;font-size:12px;text-decoration:none}.vz-sidebar .new-btn{justify-content:right;margin-right:10px;cursor:pointer}.vz-sidebar .utils{display:flex}.vz-sidebar .utilities{margin-right:3px}.vz-sidebar .file-or-directory:hover,.vz-sidebar .settings:hover{background-color:#4a5469}.vz-sidebar .name{display:flex;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.vz-sidebar .indentation{margin-left:20px}.vz-sidebar .arrow-wrapper{margin-right:4px}.vz-sidebar .settings{height:30px;color:#dbdde1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:14px 0;margin:10px;border-radius:5px}:root{--vzcode-font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:rgb(41,44,52)}::-webkit-scrollbar-thumb{background:rgb(37,37,38)}::-webkit-scrollbar-thumb:hover{background:#555}html,body,#root,.app{height:100%}.app{background-color:#292c34;font-family:Poppins,sans-serif;display:flex}.app .left{display:flex}.app .right{display:flex;flex:1;flex-direction:column;min-width:0}.app .right .tab-list{display:flex;background-color:#252526}.app .right .tab-list .tab{background:rgb(45,45,45);color:#fff;font-family:var(--vzcode-font-family);align-items:center;display:flex;padding-top:2px;padding-left:10px;padding-right:10px;border-top-left-radius:10px;border-top-right-radius:10px;margin-top:5px;margin-left:2px;margin-right:2px;cursor:pointer}.app .right .tab-list .tab .tab-close{margin-left:5px}.app .right .tab-list .tab .tab-close:hover{border-radius:30%;background-color:#252526}.app .right .tab-list .tab.active{background-color:#292c34}.app .right .editor{flex:1;display:flex;font-family:var(--vzcode-font-family);overflow-x:auto}.themes{color:#252526}
package/dist/index.html CHANGED
@@ -4,8 +4,22 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>VZCode</title>
7
- <script type="module" crossorigin src="/assets/index-523ecad7.js"></script>
8
- <link rel="stylesheet" href="/assets/index-29b6a606.css">
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ <link
14
+ href="https://unpkg.com/boxicons@2.0.7/css/boxicons.min.css"
15
+ rel="stylesheet"
16
+ />
17
+ <link
18
+ href="https://unpkg.com/bootstrap@5.3.0/dist/css/bootstrap.min.css"
19
+ rel="stylesheet"
20
+ />
21
+ <script type="module" crossorigin src="/assets/index-41607816.js"></script>
22
+ <link rel="stylesheet" href="/assets/index-8bac68c9.css">
9
23
  </head>
10
24
  <body>
11
25
  <div id="root"></div>
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "vzcode",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Multiplayer code editor system",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
+ "types": "src/types.ts",
7
8
  "bin": "src/server/index.js",
8
9
  "files": [
9
10
  "dist/**",
@@ -13,6 +14,7 @@
13
14
  "test": "vitest",
14
15
  "test-interactive": "npm run build; cd test/sampleDirectories/kitchenSink; node ../../../src/server/index.js",
15
16
  "prettier": "prettier {*.*,**/*.*} --write",
17
+ "tsc": "tsc --noEmit",
16
18
  "dev": "vite",
17
19
  "build": "vite build",
18
20
  "build-release": "vite build --mode release",
@@ -44,36 +46,39 @@
44
46
  },
45
47
  "homepage": "https://github.com/vizhub-core/vzcode#readme",
46
48
  "dependencies": {
47
- "@codemirror/lang-css": "^6.1.1",
48
- "@codemirror/lang-html": "^6.4.2",
49
- "@codemirror/lang-javascript": "^6.1.4",
50
- "@codemirror/lang-markdown": "^6.1.0",
51
- "@codemirror/state": "^6.2.0",
52
- "@codemirror/theme-one-dark": "^6.1.1",
49
+ "@codemirror/lang-css": "^6.2.0",
50
+ "@codemirror/lang-html": "^6.4.4",
51
+ "@codemirror/lang-javascript": "^6.1.9",
52
+ "@codemirror/lang-markdown": "^6.1.1",
53
+ "@codemirror/state": "^6.2.1",
54
+ "@codemirror/theme-one-dark": "^6.1.2",
53
55
  "@teamwork/websocket-json-stream": "^2.0.0",
56
+ "@uiw/codemirror-themes-all": "^4.20.2",
54
57
  "codemirror": "^6.0.1",
55
58
  "codemirror-ot": "^4.0.0",
56
- "d3-array": "^3.2.3",
59
+ "d3-array": "^3.2.4",
57
60
  "diff-match-patch": "^1.0.5",
58
- "dotenv": "^16.0.3",
61
+ "dotenv": "^16.3.0",
59
62
  "express": "^4.18.2",
60
63
  "json0-ot-diff": "^1.1.2",
61
64
  "ngrok": "^4.3.3",
62
- "open": "^9.0.0",
65
+ "open": "^9.1.0",
63
66
  "react": "^18.2.0",
64
- "react-bootstrap": "^2.7.2",
67
+ "react-bootstrap": "^2.7.4",
65
68
  "react-dom": "^18.2.0",
66
- "react-select": "^5.7.1",
67
- "sharedb": "^3.3.0",
68
- "sharedb-client-browser": "^4.2.0",
69
+ "react-select": "^5.7.3",
70
+ "sharedb": "^4.0.0",
71
+ "sharedb-client-browser": "^4.2.1",
69
72
  "ws": "^8.13.0"
70
73
  },
71
74
  "devDependencies": {
72
- "@types/react": "^18.0.28",
73
- "@types/react-dom": "^18.0.11",
74
- "@vitejs/plugin-react": "^3.1.0",
75
- "prettier": "^2.8.6",
76
- "vite": "^4.2.1",
77
- "vitest": "^0.30.1"
75
+ "@types/react": "^18.2.12",
76
+ "@types/react-dom": "^18.2.5",
77
+ "@vitejs/plugin-react": "^4.0.0",
78
+ "prettier": "^2.8.8",
79
+ "sass": "^1.64.0",
80
+ "vite": "^4.3.9",
81
+ "vitest": "^0.32.2",
82
+ "typescript": "^5.1.6"
78
83
  }
79
84
  }
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useMemo, useCallback } from 'react';
1
+ import { useState, useEffect, useCallback } from 'react';
2
2
  import ShareDBClient from 'sharedb-client-browser/dist/sharedb-client-umd.cjs';
3
3
  import { json1Presence } from '../ot';
4
4
  import { randomId } from '../randomId';
@@ -6,7 +6,9 @@ import { CodeEditor } from './CodeEditor';
6
6
  import { diff } from './diff';
7
7
  import { Settings } from './settings';
8
8
  import { Sidebar } from './Sidebar';
9
- import './style.css';
9
+ import './style.scss';
10
+ import { oneDark } from '@codemirror/theme-one-dark';
11
+ import { FileId } from '../types';
10
12
 
11
13
  // Register our custom JSON1 OT type that supports presence.
12
14
  // See https://github.com/vizhub-core/json1-presence
@@ -36,9 +38,6 @@ function App() {
36
38
  // updated on each change to decouple rendering from ShareDB.
37
39
  const [data, setData] = useState(null);
38
40
 
39
- // True if the file menu is open.
40
- const [isFileMenuOpen, setIsFileMenuOpen] = useState(false);
41
-
42
41
  // The id of the currently open file tab.
43
42
  const [activeFileId, setActiveFileId] = useState(null);
44
43
 
@@ -46,10 +45,10 @@ function App() {
46
45
  const [tabList, setTabList] = useState([]);
47
46
 
48
47
  // The current theme.
49
- const [theme, setTheme] = useState('OneDark');
48
+ const [theme, setTheme] = useState(oneDark);
50
49
 
51
50
  // True to show the settings modal.
52
- const [settings, setSettings] = useState(false);
51
+ const [isSettingsOpen, setIsSettingsOpen] = useState(false);
53
52
 
54
53
  // Set up the connection to ShareDB.
55
54
  useEffect(() => {
@@ -138,14 +137,14 @@ function App() {
138
137
 
139
138
  // Called when a file in the sidebar is double-clicked.
140
139
  const handleRenameFileClick = useCallback(
141
- (key) => {
140
+ (fileId: FileId) => {
142
141
  const newName = prompt('Enter new name');
143
142
  if (newName) {
144
143
  const currentDocument = shareDBDoc.data;
145
144
  const nextDocument = {
146
145
  ...currentDocument,
147
- [key]: {
148
- ...currentDocument[key],
146
+ [fileId]: {
147
+ ...currentDocument[fileId],
149
148
  name: newName,
150
149
  },
151
150
  };
@@ -156,21 +155,21 @@ function App() {
156
155
  );
157
156
 
158
157
  const deleteFile = useCallback(
159
- (key) => {
158
+ (fileId: FileId) => {
160
159
  // Close the tab in case it's open.
161
160
  // TODO consistently use either "key" or "fileId" naming
162
- closeTab(key);
161
+ closeTab(fileId);
163
162
 
164
163
  const currentDocument = shareDBDoc.data;
165
164
  const nextDocument = { ...currentDocument };
166
- delete nextDocument[key];
165
+ delete nextDocument[fileId];
167
166
  shareDBDoc.submitOp(diff(currentDocument, nextDocument));
168
167
  },
169
168
  [shareDBDoc, closeTab]
170
169
  );
171
170
 
172
171
  const handleFileClick = useCallback(
173
- (fileId) => {
172
+ (fileId: FileId) => {
174
173
  setActiveFileId(fileId);
175
174
  if (!tabList.includes(fileId)) {
176
175
  setTabList([...tabList, fileId]);
@@ -181,7 +180,7 @@ function App() {
181
180
 
182
181
  // TODO prompt the user "Are you sure?"
183
182
  const handleDeleteFileClick = useCallback(
184
- (fileId) => (event) => {
183
+ (fileId: FileId, event: React.MouseEvent) => {
185
184
  // Stop propagation so that the outer listener doesn't fire,
186
185
  // which would try to open this file in a tab.
187
186
  event.stopPropagation();
@@ -191,6 +190,7 @@ function App() {
191
190
  );
192
191
 
193
192
  const createFile = useCallback(() => {
193
+ // TODO better UI, maybe Bootstrap modal? Maybe edit inline?
194
194
  const name = prompt('Enter new file name');
195
195
  if (!name) return;
196
196
  const currentDocument = shareDBDoc.data;
@@ -205,60 +205,68 @@ function App() {
205
205
  const tabValid = data && activeFileId;
206
206
 
207
207
  const handleSettingsClose = useCallback(() => {
208
- setSettings(false);
208
+ setIsSettingsOpen(false);
209
209
  }, []);
210
210
 
211
211
  const fileNameSplit = (fileName) => {
212
212
  const split = fileName.split('/');
213
- return split[split.length - 1];
213
+ //adding the folder that the is in to the tab name
214
+ if (split.length === 1) return split[split.length - 1];
215
+ return split[split.length - 2] + '/' + split[split.length - 1];
214
216
  };
215
217
 
216
218
  return (
217
- <>
218
- <Settings
219
- show={settings}
220
- onClose={handleSettingsClose}
221
- setTheme={setTheme}
222
- />
223
- <div className="tab-list">
224
- {tabList.map((fileId) => (
225
- <div
226
- key={fileId}
227
- className={
228
- tabValid ? `tab${fileId === activeFileId ? ' active' : ''}` : null
229
- }
230
- onClick={() => {
231
- setActiveFileId(fileId);
232
- }}
233
- >
234
- {tabValid ? fileNameSplit(data[fileId].name) : ''}
219
+ <div className="app">
220
+ <div className="left">
221
+ <Sidebar
222
+ createFile={createFile}
223
+ files={data}
224
+ handleRenameFileClick={handleRenameFileClick}
225
+ handleDeleteFileClick={handleDeleteFileClick}
226
+ handleFileClick={handleFileClick}
227
+ setIsSettingsOpen={setIsSettingsOpen}
228
+ isSettingsOpen={isSettingsOpen}
229
+ />
230
+ <Settings
231
+ show={isSettingsOpen}
232
+ onClose={handleSettingsClose}
233
+ setTheme={setTheme}
234
+ />
235
+ </div>
236
+ <div className="right">
237
+ <div className="tab-list">
238
+ {tabList.map((fileId) => (
235
239
  <div
236
- className={activeFileId ? 'bx bx-x tab-close' : ''}
237
- onClick={handleCloseTabClick(fileId)}
238
- ></div>
239
- </div>
240
- ))}
240
+ key={fileId}
241
+ className={
242
+ tabValid
243
+ ? `tab${fileId === activeFileId ? ' active' : ''}`
244
+ : null
245
+ }
246
+ onClick={() => {
247
+ setActiveFileId(fileId);
248
+ }}
249
+ >
250
+ {tabValid ? fileNameSplit(data[fileId].name) : ''}
251
+ <div
252
+ className={activeFileId ? 'bx bx-x tab-close' : ''}
253
+ onClick={handleCloseTabClick(fileId)}
254
+ ></div>
255
+ </div>
256
+ ))}
257
+ </div>
258
+ {data && activeFileId ? (
259
+ <CodeEditor
260
+ className="editor"
261
+ shareDBDoc={shareDBDoc}
262
+ localPresence={localPresence}
263
+ docPresence={docPresence}
264
+ activeFileId={activeFileId}
265
+ theme={theme}
266
+ />
267
+ ) : null}
241
268
  </div>
242
- <Sidebar
243
- createFile={createFile}
244
- data={data}
245
- handleRenameFileClick={handleRenameFileClick}
246
- handleDeleteFileClick={handleDeleteFileClick}
247
- handleFileClick={handleFileClick}
248
- setSettings={setSettings}
249
- settings={settings}
250
- />
251
- {data && activeFileId ? (
252
- <CodeEditor
253
- className="editor"
254
- shareDBDoc={shareDBDoc}
255
- localPresence={localPresence}
256
- docPresence={docPresence}
257
- activeFileId={activeFileId}
258
- theme={theme}
259
- />
260
- ) : null}
261
- </>
269
+ </div>
262
270
  );
263
271
  }
264
272
 
@@ -1,9 +1,10 @@
1
1
  import { useCallback } from 'react';
2
2
  import { Item } from './Item';
3
- import { FileOrDirectory } from './FileOrDirectory';
3
+ import { Listing } from './Listing';
4
4
  import { DirectoryArrowSVG } from './DirectoryArrowSVG';
5
+ import { FileTree, FileTreeFile } from '../../types';
5
6
 
6
- export const Directory = ({
7
+ export const DirectoryListing = ({
7
8
  name,
8
9
  path,
9
10
  children,
@@ -12,6 +13,15 @@ export const Directory = ({
12
13
  handleFileClick,
13
14
  openDirectories,
14
15
  toggleDirectory,
16
+ }: {
17
+ name: string;
18
+ path: string;
19
+ children: Array<FileTree | FileTreeFile>;
20
+ handleRenameFileClick: (fileId: string) => void;
21
+ handleDeleteFileClick: (fileId: string, event: React.MouseEvent) => void;
22
+ handleFileClick: (fileId: string) => void;
23
+ openDirectories: { [path: string]: boolean };
24
+ toggleDirectory: (path: string) => void;
15
25
  }) => {
16
26
  const handleClick = useCallback(() => {
17
27
  toggleDirectory(path);
@@ -44,17 +54,21 @@ export const Directory = ({
44
54
  </Item>
45
55
  {children && isOpen ? (
46
56
  <div className="indentation">
47
- {children.map((entity) => (
48
- <FileOrDirectory
49
- entity={entity}
50
- key={entity.fileId || entity.path}
51
- handleRenameFileClick={handleRenameFileClick}
52
- handleDeleteFileClick={handleDeleteFileClick}
53
- handleFileClick={handleFileClick}
54
- openDirectories={openDirectories}
55
- toggleDirectory={toggleDirectory}
56
- />
57
- ))}
57
+ {children.map((entity) => {
58
+ const { fileId } = entity as FileTreeFile;
59
+ const { path } = entity as FileTree;
60
+ return (
61
+ <Listing
62
+ entity={entity}
63
+ key={fileId || path}
64
+ handleRenameFileClick={handleRenameFileClick}
65
+ handleDeleteFileClick={handleDeleteFileClick}
66
+ handleFileClick={handleFileClick}
67
+ openDirectories={openDirectories}
68
+ toggleDirectory={toggleDirectory}
69
+ />
70
+ );
71
+ })}
58
72
  </div>
59
73
  ) : null}
60
74
  </>
@@ -1,20 +1,30 @@
1
1
  import { useCallback } from 'react';
2
2
  import { Item } from './Item';
3
+ import { FileId } from '../../types';
3
4
 
4
- export const File = ({
5
+ export const FileListing = ({
5
6
  name,
6
7
  fileId,
7
8
  handleRenameFileClick,
8
9
  handleDeleteFileClick,
9
10
  handleFileClick,
11
+ }: {
12
+ name: string;
13
+ fileId: FileId;
14
+ handleRenameFileClick: (fileId: FileId) => void;
15
+ handleDeleteFileClick: (fileId: FileId, event: React.MouseEvent) => void;
16
+ handleFileClick: (fileId: FileId) => void;
10
17
  }) => {
11
18
  const handleClick = useCallback(() => {
12
19
  handleFileClick(fileId);
13
20
  }, [fileId, handleFileClick]);
14
21
 
15
- const handleDeleteClick = useCallback(() => {
16
- handleDeleteFileClick(fileId);
17
- }, [fileId]);
22
+ const handleDeleteClick = useCallback(
23
+ (event) => {
24
+ handleDeleteFileClick(fileId, event);
25
+ },
26
+ [fileId]
27
+ );
18
28
 
19
29
  const handleRenameClick = useCallback(() => {
20
30
  handleRenameFileClick(fileId);
@@ -0,0 +1,44 @@
1
+ import { DirectoryListing } from './DirectoryListing';
2
+ import { FileId, FileTree, FileTreeFile } from '../../types';
3
+ import { FileListing } from './FileListing';
4
+
5
+ // A "Listing" is a "FileListing" or a "DirectoryListing"
6
+ // that appears in the Sidebar.
7
+ export const Listing = ({
8
+ entity,
9
+ handleRenameFileClick,
10
+ handleDeleteFileClick,
11
+ handleFileClick,
12
+ openDirectories,
13
+ toggleDirectory,
14
+ }: {
15
+ entity: FileTree | FileTreeFile;
16
+ handleRenameFileClick: (fileId: FileId) => void;
17
+ handleDeleteFileClick: (fileId: FileId, event: React.MouseEvent) => void;
18
+ handleFileClick: (fileId: FileId) => void;
19
+ openDirectories: { [path: string]: boolean };
20
+ toggleDirectory: (path: string) => void;
21
+ }) => {
22
+ const { name, file, fileId } = entity as FileTreeFile;
23
+ const { path, children } = entity as FileTree;
24
+ return file ? (
25
+ <FileListing
26
+ fileId={fileId}
27
+ name={name}
28
+ handleRenameFileClick={handleRenameFileClick}
29
+ handleDeleteFileClick={handleDeleteFileClick}
30
+ handleFileClick={handleFileClick}
31
+ />
32
+ ) : (
33
+ <DirectoryListing
34
+ name={name}
35
+ path={path}
36
+ children={children}
37
+ handleRenameFileClick={handleRenameFileClick}
38
+ handleDeleteFileClick={handleDeleteFileClick}
39
+ handleFileClick={handleFileClick}
40
+ openDirectories={openDirectories}
41
+ toggleDirectory={toggleDirectory}
42
+ />
43
+ );
44
+ };
@@ -0,0 +1,80 @@
1
+ import { useMemo } from 'react';
2
+ import { getFileTree } from '../getFileTree';
3
+ import { sortFileTree } from '../sortFileTree';
4
+ import { disableSettings } from '../featureFlags';
5
+ import { Listing } from './Listing';
6
+ import { useOpenDirectories } from './useOpenDirectories';
7
+
8
+ import './styles.scss';
9
+ import { FileId, FileTree, FileTreeFile, Files } from '../../types';
10
+
11
+ export const Sidebar = ({
12
+ createFile,
13
+ files,
14
+ handleRenameFileClick,
15
+ handleDeleteFileClick,
16
+ handleFileClick,
17
+ setIsSettingsOpen,
18
+ isSettingsOpen,
19
+ }: {
20
+ files: Files;
21
+ createFile?: () => void;
22
+ handleRenameFileClick?: (fileId: FileId) => void;
23
+ handleDeleteFileClick?: (fileId: FileId, event: React.MouseEvent) => void;
24
+ handleFileClick?: (fileId: FileId) => void;
25
+ setIsSettingsOpen?: (isSettingsOpen: boolean) => void;
26
+ isSettingsOpen?: boolean;
27
+ }) => {
28
+ const fileTree = useMemo(
29
+ () => (files ? sortFileTree(getFileTree(files)) : null),
30
+ [files]
31
+ );
32
+
33
+ const { openDirectories, toggleDirectory } = useOpenDirectories();
34
+
35
+ return (
36
+ <div className="vz-sidebar">
37
+ <div className="files">
38
+ <div className="full-box">
39
+ <div>
40
+ <a className="link-name" href="#">
41
+ Files
42
+ </a>
43
+ </div>
44
+ <div>
45
+ <i
46
+ className="bx bxs-file-plus new-btn"
47
+ style={{ color: '#dbdde1' }}
48
+ onClick={createFile}
49
+ ></i>
50
+ </div>
51
+ </div>
52
+ {fileTree
53
+ ? fileTree.children.map((entity) => {
54
+ const { fileId } = entity as FileTreeFile;
55
+ const { path } = entity as FileTree;
56
+ return (
57
+ <Listing
58
+ entity={entity}
59
+ key={fileId || path}
60
+ handleRenameFileClick={handleRenameFileClick}
61
+ handleDeleteFileClick={handleDeleteFileClick}
62
+ handleFileClick={handleFileClick}
63
+ openDirectories={openDirectories}
64
+ toggleDirectory={toggleDirectory}
65
+ />
66
+ );
67
+ })
68
+ : null}
69
+ </div>
70
+ {disableSettings ? null : (
71
+ <div
72
+ className="settings"
73
+ onClick={() => setIsSettingsOpen(!isSettingsOpen)}
74
+ >
75
+ Settings
76
+ </div>
77
+ )}
78
+ </div>
79
+ );
80
+ };
@@ -0,0 +1,84 @@
1
+ .vz-sidebar {
2
+ height: 100%;
3
+ width: 200px;
4
+ background: rgb(54, 65, 89);
5
+ display: flex;
6
+ flex-direction: column;
7
+ justify-content: space-between;
8
+
9
+ .full-box {
10
+ padding: 7px;
11
+ display: flex;
12
+ flex-direction: row;
13
+ align-items: center;
14
+ justify-content: space-between;
15
+ }
16
+
17
+ .files {
18
+ overflow: auto;
19
+ }
20
+
21
+ .file-or-directory {
22
+ display: flex;
23
+ flex-direction: row;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ cursor: pointer;
27
+ margin: 0 10px;
28
+ color: rgb(219, 221, 225);
29
+ border-radius: 5px;
30
+ }
31
+
32
+ .link-name {
33
+ margin-top: 30px;
34
+ margin-left: 10px;
35
+ color: rgb(154, 160, 172);
36
+ font-size: 12px;
37
+ text-decoration: none;
38
+ }
39
+
40
+ .new-btn {
41
+ justify-content: right;
42
+ margin-right: 10px;
43
+ cursor: pointer;
44
+ }
45
+
46
+ .utils {
47
+ display: flex;
48
+ }
49
+ .utilities {
50
+ margin-right: 3px;
51
+ }
52
+
53
+ .file-or-directory:hover,
54
+ .settings:hover {
55
+ background-color: rgb(74, 84, 105);
56
+ }
57
+
58
+ .name {
59
+ display: flex;
60
+ text-overflow: ellipsis;
61
+ white-space: nowrap;
62
+ overflow: hidden;
63
+ }
64
+
65
+ .indentation {
66
+ margin-left: 20px;
67
+ }
68
+
69
+ .arrow-wrapper {
70
+ margin-right: 4px;
71
+ }
72
+
73
+ .settings {
74
+ height: 30px;
75
+ color: rgb(219, 221, 225);
76
+ cursor: pointer;
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ padding: 14px 0;
81
+ margin: 10px;
82
+ border-radius: 5px;
83
+ }
84
+ }
@@ -17,16 +17,14 @@ import { useState, useCallback } from 'react';
17
17
 
18
18
  // Inspired by
19
19
  // https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/useOpenDirectories.js
20
- export const useOpenDirectories = (activeFile) => {
21
- //const [openDirectories, setOpenDirectories] = useState(
22
- // initialOpenDirectories(activeFile)
23
- //);
24
- const [openDirectories, setOpenDirectories] = useState({});
20
+ export const useOpenDirectories = () => {
21
+ const [openDirectories, setOpenDirectories] = useState<{
22
+ [path: string]: boolean;
23
+ }>({});
25
24
 
26
25
  // TODO consider useReducer to avoid event listener churn
27
26
  const toggleDirectory = useCallback(
28
27
  (path) => {
29
- console.log(openDirectories[path], 'here');
30
28
  setOpenDirectories(
31
29
  Object.assign({}, openDirectories, {
32
30
  [path]: !openDirectories[path],
@@ -1,7 +1,10 @@
1
1
  // Inspired by
2
+
3
+ import { Files, FileTree } from '../types';
4
+
2
5
  // https://github.com/vizhub-core/vizhub/blob/main/vizhub-v2/packages/neoFrontend/src/pages/VizPage/Body/Editor/FilesSection/FileTree/getFileTree.js
3
- export const getFileTree = (files) => {
4
- const tree = { name: 'files' };
6
+ export const getFileTree = (files: Files): FileTree => {
7
+ const tree: FileTree = { name: 'files' };
5
8
  for (const fileId of Object.keys(files)) {
6
9
  const file = files[fileId];
7
10
  const path = file.name.split('/');
@@ -11,7 +14,7 @@ export const getFileTree = (files) => {
11
14
  // Walk the path.
12
15
  for (let i = 0; i < n - 1; i++) {
13
16
  // Search for an existing child.
14
- let child;
17
+ let child: FileTree | undefined;
15
18
  const name = path[i];
16
19
  if (node.children) {
17
20
  for (let j = 0; j < node.children.length; j++) {