diff options
author | elioat <elioat@tilde.institute> | 2024-12-22 21:29:35 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2024-12-22 21:29:35 -0500 |
commit | 8846eb4d7a16bd905547fa7567f539aa901dd92a (patch) | |
tree | 5088ac337d72e7af3f854255af30b07ad078b29b /html/file-system | |
parent | 7391ee4f44b084abdd987a529ff94a9c813c68b5 (diff) | |
download | tour-8846eb4d7a16bd905547fa7567f539aa901dd92a.tar.gz |
*
Diffstat (limited to 'html/file-system')
-rw-r--r-- | html/file-system/index.html | 162 |
1 files changed, 158 insertions, 4 deletions
diff --git a/html/file-system/index.html b/html/file-system/index.html index 397427f..45bfaaf 100644 --- a/html/file-system/index.html +++ b/html/file-system/index.html @@ -427,6 +427,81 @@ .folder.editing input:focus { border-color: #0078fa; } + + #viewControls { + position: absolute; + top: 1em; + right: 1em; + } + + .view-toggle { + padding: 0.5em; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + cursor: pointer; + transition: background-color 0.2s; + } + + .view-toggle:hover { + background-color: #f0f0f0; + } + + #breadcrumbs { + padding: 0.5em; + margin-bottom: 1em; + border-bottom: 1px solid #e0e0e0; + display: none; /* Hidden in tree view */ + } + + .breadcrumb-item { + display: inline-block; + cursor: pointer; + padding: 0.2em 0.5em; + border-radius: 3px; + } + + .breadcrumb-item:hover { + background-color: rgba(0, 0, 0, 0.05); + } + + .breadcrumb-separator { + margin: 0 0.5em; + color: #666; + } + + /* Grid view styles */ + .grid-view { + display: none; /* Hidden by default */ + grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); + gap: 1em; + padding: 1em; + } + + .grid-folder { + display: flex; + flex-direction: column; + align-items: center; + padding: 1em; + cursor: pointer; + border-radius: 4px; + text-align: center; + } + + .grid-folder:hover { + background-color: rgba(0, 0, 0, 0.05); + } + + .grid-folder-icon { + font-size: 2em; + margin-bottom: 0.5em; + } + + .grid-folder-name { + font-size: 0.9em; + word-break: break-word; + max-width: 100%; + } </style> </head> <body> @@ -443,6 +518,7 @@ <div id="fileSystemContainer" oncontextmenu="showContextMenu(event, 'root')"> <div class="root-folder" onclick="selectFolder('root')">~</div> <ul id="fileSystemTree"></ul> + <div class="grid-view"></div> <div class="empty-state" style="display: none;"> <div class="empty-state-text">No folders yet</div> <div class="empty-state-hint">Right-click or use the menu to create a folder</div> @@ -472,6 +548,14 @@ <div class="context-menu-item delete" onclick="deleteItem()">Delete</div> </div> +<div id="viewControls"> + <button class="view-toggle" onclick="toggleView()" title="Toggle View"> + <span class="tree-icon">🌳</span>/<span class="grid-icon">📱</span> + </button> +</div> + +<div id="breadcrumbs"></div> + <script> const initialState = () => ({ root: { type: 'folder', children: {}, content: '' } @@ -484,7 +568,8 @@ const state = { currentFolderPath: 'root', copiedFolderData: null, copiedItemPath: null, - draggedPath: null + draggedPath: null, + viewMode: 'tree' // 'tree' or 'grid' }; const clone = (obj) => JSON.parse(JSON.stringify(obj)); @@ -751,9 +836,28 @@ const render = () => { return; } - const entries = Object.entries(state.fileSystem.root.children); - const isEmpty = entries.length === 0; - + // Show/hide breadcrumbs based on view mode + document.getElementById('breadcrumbs').style.display = + state.viewMode === 'grid' ? 'block' : 'none'; + + // Update current folder content based on view mode + if (state.viewMode === 'tree') { + document.getElementById('fileSystemTree').style.display = 'block'; + document.querySelector('.grid-view').style.display = 'none'; + document.getElementById('fileSystemTree').innerHTML = + renderFolderTree(state.fileSystem.root); + } else { + document.getElementById('fileSystemTree').style.display = 'none'; + document.querySelector('.grid-view').style.display = 'grid'; + const currentFolder = state.currentFolderPath === 'root' ? + state.fileSystem.root : + getFolder(state.currentFolderPath); + document.querySelector('.grid-view').innerHTML = + renderGridView(currentFolder); + } + + renderBreadcrumbs(); + // Update path displays const currentPath = state.currentFolderPath.replace('root', '') || '/'; document.title = `Folder: ${currentPath}`; @@ -1018,6 +1122,56 @@ const handleFolderDoubleClick = (e) => { document.addEventListener('dblclick', handleFolderDoubleClick); +const toggleView = () => { + state.viewMode = state.viewMode === 'tree' ? 'grid' : 'tree'; + render(); +}; + +const navigateTo = (path, openDetails = false) => { + state.currentFolderPath = path; + + // Only open folder details if explicitly requested (from breadcrumbs) + if (openDetails) { + const folder = path === 'root' ? state.fileSystem.root : getFolder(path); + if (folder) { + document.getElementById('folderContent').value = folder.content || ''; + editor.style.display = 'block'; + setEditorSize('normal'); + } + } + + render(); +}; + +const renderBreadcrumbs = () => { + const parts = state.currentFolderPath.split('/'); + const breadcrumbs = parts.map((part, index) => { + const path = parts.slice(0, index + 1).join('/'); + const isLastItem = index === parts.length - 1; + + // Only pass openDetails=true for the last item in the breadcrumb + return ` + <span class="breadcrumb-item" + onclick="navigateTo('${path}', ${isLastItem})">${part === 'root' ? '~' : part}</span> + ${index < parts.length - 1 ? '<span class="breadcrumb-separator">/</span>' : ''} + `; + }).join(''); + + document.getElementById('breadcrumbs').innerHTML = breadcrumbs; +}; + +const renderGridView = (folder) => { + const entries = Object.entries(folder.children); + return entries.map(([name, item]) => ` + <div class="grid-folder" + onclick="navigateTo('${state.currentFolderPath}/${name}')" + oncontextmenu="showContextMenu(event, '${state.currentFolderPath}/${name}')"> + <div class="grid-folder-icon">📁</div> + <div class="grid-folder-name">${name}</div> + </div> + `).join(''); +}; + render(); </script> |