From ac9479a36f112e45da9cf897edda76c545098c7e Mon Sep 17 00:00:00 2001 From: Max Mykal Date: Thu, 6 Apr 2023 10:54:47 -0700 Subject: [PATCH 1/3] Fix file not selected after tab closed and create .sql file by default in models --- web/client/src/context/fileTree.ts | 2 +- .../library/components/fileTree/Directory.tsx | 3 +- .../src/library/components/fileTree/File.tsx | 170 ++++++++++++------ web/client/src/models/directory.ts | 4 + 4 files changed, 117 insertions(+), 62 deletions(-) diff --git a/web/client/src/context/fileTree.ts b/web/client/src/context/fileTree.ts index f6d75b4e8b..d3a49c5c18 100644 --- a/web/client/src/context/fileTree.ts +++ b/web/client/src/context/fileTree.ts @@ -8,8 +8,8 @@ interface FileTreeStore { selectedFile?: ModelFile selectFile: (selectedFile: ModelFile) => void setFiles: (files: ModelFile[]) => void - refreshProject: () => void setProject: (project?: Directory) => void + refreshProject: () => void } export const useStoreFileTree = create((set, get) => ({ diff --git a/web/client/src/library/components/fileTree/Directory.tsx b/web/client/src/library/components/fileTree/Directory.tsx index e5e95abdce..d52bf4636e 100644 --- a/web/client/src/library/components/fileTree/Directory.tsx +++ b/web/client/src/library/components/fileTree/Directory.tsx @@ -97,7 +97,7 @@ export default function Directory({ setIsLoading(true) - const extension = '.py' + const extension = directory.isModels ? '.sql' : '.py' const name = toUniqueName('new_file', extension) writeFileApiFilesPathPost(`${directory.path}/${name}`, { content: '' }) @@ -109,7 +109,6 @@ export default function Directory({ } directory.addFile(new ModelFile(created, directory)) - directory.open() }) .catch(error => { diff --git a/web/client/src/library/components/fileTree/File.tsx b/web/client/src/library/components/fileTree/File.tsx index ff9f7b987e..8c5c62cb08 100644 --- a/web/client/src/library/components/fileTree/File.tsx +++ b/web/client/src/library/components/fileTree/File.tsx @@ -1,4 +1,4 @@ -import { useState, type MouseEvent } from 'react' +import { useState, type MouseEvent, useEffect } from 'react' import { DocumentIcon, XCircleIcon, @@ -19,8 +19,6 @@ interface PropsFile extends WithConfirmation { file: ModelFile } -const CSS_ICON_SIZE = 'w-4 h-4' - export default function File({ file, setConfirmation, @@ -29,12 +27,17 @@ export default function File({ const tabs = useStoreEditor(s => s.tabs) const closeTab = useStoreEditor(s => s.closeTab) + const selectedFile = useStoreFileTree(s => s.selectedFile) const selectFile = useStoreFileTree(s => s.selectFile) const refreshProject = useStoreFileTree(s => s.refreshProject) const [isLoading, setIsLoading] = useState(false) const [newName, setNewName] = useState() + useEffect(() => { + selectFile(tab.file) + }, [tab]) + function remove(): void { if (isLoading) return @@ -107,14 +110,14 @@ export default function File({ {isStringEmptyOrNil(newName) ? ( <> - { - e.stopPropagation() - - file.is_supported && file !== tab.file && selectFile(file) - }} - onDoubleClick={(e: MouseEvent) => { - e.stopPropagation() - - setNewName(file.name) - }} - className={clsx( - 'w-full overflow-hidden overflow-ellipsis cursor-default', - !file.is_supported && 'opacity-50 cursor-not-allowed', - )} - > - {file.name} - - { - e.stopPropagation() - - removeWithConfirmation() - }} - > - - + + ) : ( -
- { - e.stopPropagation() - - setNewName(e.target.value) - }} - /> -
- { - e.stopPropagation() - - rename() - }} - /> -
-
+ )}
) } + +function FileName({ + file, + setNewName, +}: { + file: ModelFile + setNewName: (name: string) => void +}): JSX.Element { + const selectedFile = useStoreFileTree(s => s.selectedFile) + const selectFile = useStoreFileTree(s => s.selectFile) + + return ( + { + e.stopPropagation() + + file.is_supported && file !== selectedFile && selectFile(file) + }} + onDoubleClick={(e: MouseEvent) => { + e.stopPropagation() + + setNewName(file.name) + }} + className={clsx( + 'w-full overflow-hidden overflow-ellipsis cursor-default', + !file.is_supported && 'opacity-50 cursor-not-allowed', + )} + > + {file.name} + + ) +} + +function FileRename({ + file, + newName, + setNewName, + rename, +}: { + file: ModelFile + newName?: string + setNewName: (name: string) => void + rename: () => void +}): JSX.Element { + return ( +
+ { + e.stopPropagation() + + setNewName(e.target.value) + }} + /> +
+ { + e.stopPropagation() + + rename() + }} + /> +
+
+ ) +} + +function FileActions({ + removeWithConfirmation, +}: { + removeWithConfirmation: () => void +}): JSX.Element { + return ( + { + e.stopPropagation() + + removeWithConfirmation() + }} + > + + + ) +} diff --git a/web/client/src/models/directory.ts b/web/client/src/models/directory.ts index e4d9676906..597b2df941 100644 --- a/web/client/src/models/directory.ts +++ b/web/client/src/models/directory.ts @@ -93,6 +93,10 @@ export class ModelDirectory extends ModelArtifact { return !this.isOpen && this.allDirectories.every(d => d.isCollapsed) } + get isModels(): boolean { + return this.path.startsWith('models') + } + open(): void { this._isOpen = true From e71a06b1a55822d2192ee68eaa2ad92c47018c40 Mon Sep 17 00:00:00 2001 From: Max Mykal Date: Thu, 6 Apr 2023 13:38:23 -0700 Subject: [PATCH 2/3] Fix issue: renaming file creates a duplicate editor tab on UI --- web/client/src/context/editor.ts | 18 +++++++++--------- .../library/components/editor/EditorCode.tsx | 4 ++-- .../library/components/editor/EditorTabs.tsx | 2 +- .../library/components/fileTree/Directory.tsx | 2 +- .../src/library/components/fileTree/File.tsx | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/web/client/src/context/editor.ts b/web/client/src/context/editor.ts index d6bbcd8233..d998c945ba 100644 --- a/web/client/src/context/editor.ts +++ b/web/client/src/context/editor.ts @@ -11,7 +11,7 @@ export interface Dialect { interface EditorStore { storedTabsIds: ID[] - tabs: Map + tabs: Map tab: EditorTab engine: Worker dialects: Dialect[] @@ -20,7 +20,7 @@ interface EditorStore { previewConsole?: string selectTab: (tab: EditorTab) => void addTab: (tab: EditorTab) => void - closeTab: (id: ID) => void + closeTab: (file: ModelFile) => void createTab: (file?: ModelFile) => EditorTab setDialects: (dialects: Dialect[]) => void refreshTab: () => void @@ -48,7 +48,7 @@ const [getStoredTabs, setStoredTabs] = useLocalStorage<{ ids: ID[] }>('tabs') const initialFile = createLocalFile() const initialTab: EditorTab = createTab(initialFile, true) -const initialTabs = new Map([[initialFile.id, initialTab]]) +const initialTabs = new Map([[initialFile, initialTab]]) export const useStoreEditor = create((set, get) => ({ storedTabsIds: getStoredTabsIds(), @@ -70,7 +70,7 @@ export const useStoreEditor = create((set, get) => ({ get().addTab(tab) }, addTab(tab) { - const tabs = new Map([...get().tabs, [tab.file.id, tab]]) + const tabs = new Map([...get().tabs, [tab.file, tab]]) setStoredTabs({ ids: Array.from(tabs.values()) @@ -83,17 +83,17 @@ export const useStoreEditor = create((set, get) => ({ tabs, })) }, - closeTab(id) { + closeTab(file) { const s = get() - if (isTrue(s.tabs.get(id)?.isInitial)) return + if (isTrue(s.tabs.get(file)?.isInitial)) return const tabs = Array.from(get().tabs.values()) - const indexAt = tabs.findIndex(tab => tab.file.id === id) + const indexAt = tabs.findIndex(tab => tab.file === file) - s.tabs.delete(id) + s.tabs.delete(file) - if (id === s.tab.file.id) { + if (file.id === s.tab.file.id) { s.selectTab(tabs.at(indexAt - 1) as EditorTab) } diff --git a/web/client/src/library/components/editor/EditorCode.tsx b/web/client/src/library/components/editor/EditorCode.tsx index ee1c3363de..dbe674129d 100644 --- a/web/client/src/library/components/editor/EditorCode.tsx +++ b/web/client/src/library/components/editor/EditorCode.tsx @@ -104,13 +104,13 @@ export default function CodeEditor(): JSX.Element { key: 'Mod-Alt-]', preventDefault: true, run() { - closeTab(tab.file.id) + closeTab(tab.file) return true }, }, ], - [closeTab, selectTab, createTab, tab.file.id], + [closeTab, selectTab, createTab, tab.file], ) useEffect(() => { diff --git a/web/client/src/library/components/editor/EditorTabs.tsx b/web/client/src/library/components/editor/EditorTabs.tsx index ceb52e9cef..375b270037 100644 --- a/web/client/src/library/components/editor/EditorTabs.tsx +++ b/web/client/src/library/components/editor/EditorTabs.tsx @@ -81,7 +81,7 @@ function Tab({ const closeTab = useStoreEditor(s => s.closeTab) function closeEditorTab(tab: EditorTab): void { - closeTab(tab.file.id) + closeTab(tab.file) } return ( diff --git a/web/client/src/library/components/fileTree/Directory.tsx b/web/client/src/library/components/fileTree/Directory.tsx index d52bf4636e..8f45511a8d 100644 --- a/web/client/src/library/components/fileTree/Directory.tsx +++ b/web/client/src/library/components/fileTree/Directory.tsx @@ -140,7 +140,7 @@ export default function Directory({ const files = getAllFilesInDirectory(directory) files.forEach(file => { - closeTab(file.id) + closeTab(file) }) } diff --git a/web/client/src/library/components/fileTree/File.tsx b/web/client/src/library/components/fileTree/File.tsx index 8c5c62cb08..6797e35475 100644 --- a/web/client/src/library/components/fileTree/File.tsx +++ b/web/client/src/library/components/fileTree/File.tsx @@ -46,7 +46,7 @@ export default function File({ deleteFileApiFilesPathDelete(file.path) .then(response => { if ((response as unknown as { ok: boolean }).ok) { - closeTab(file.id) + closeTab(file) file.parent?.removeFile(file) @@ -114,7 +114,7 @@ export default function File({ file.is_supported && 'group hover:bg-neutral-100 dark:hover:bg-dark-lighter', isFalse(isStringEmptyOrNil(newName)) && 'bg-primary-800', - tabs.has(file.id) + tabs.has(file) ? 'text-brand-500' : 'text-neutral-500 dark:text-neutral-100', file === selectedFile && 'bg-neutral-100 dark:bg-dark-lighter', From c875428c161b9e508e173b84c54b20bb5141a7a7 Mon Sep 17 00:00:00 2001 From: Max Mykal Date: Thu, 6 Apr 2023 13:48:38 -0700 Subject: [PATCH 3/3] make first local editor tab non-closeable --- web/client/src/library/components/editor/Editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/client/src/library/components/editor/Editor.tsx b/web/client/src/library/components/editor/Editor.tsx index 329e303047..9b77e60011 100644 --- a/web/client/src/library/components/editor/Editor.tsx +++ b/web/client/src/library/components/editor/Editor.tsx @@ -77,7 +77,7 @@ export default function Editor(): JSX.Element { }, [tab]) useEffect(() => { - if (selectedFile == null) return + if (selectedFile == null || tab.file === selectedFile) return selectTab(createTab(selectedFile)) }, [selectedFile])