Skip to content

Commit abd0f3c

Browse files
committed
improve file tree, fix links, cleanup
1 parent 843cce4 commit abd0f3c

6 files changed

Lines changed: 305 additions & 136 deletions

File tree

Lines changed: 141 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,106 @@
11
<template>
2-
<slot></slot>
2+
<dl>
3+
<div class="header">
4+
<span class="header">{{ props.name }}</span>
5+
<span class="header">Description</span>
6+
</div>
7+
<slot></slot>
8+
</dl>
39
</template>
410

511
<script setup lang="ts">
612
import { onMounted } from 'vue';
713
14+
const props = defineProps<{
15+
name: string;
16+
showall?: boolean;
17+
}>();
18+
819
let selectedItem: TreeItem | null = null;
920
10-
// Each TR is either a folder or a file
11-
// The second TD is the description/content of the file or folder
12-
// The first TD is clickable
13-
// If it's a folder, clicking it will show/hide the TRs below that are inside that folder
14-
// If it's a file, clicking it will show/hide the content in the second TD
21+
// Each div.file is either a folder or a file
22+
// The DD element is the description/content of the file or folder
23+
// The DT element is clickable
24+
// If it's a folder, clicking it will show/hide the divs below that are inside that folder
25+
// If it's a file, clicking it will show/hide the content in the DD element
1526
1627
const canToggleFolders = false;
1728
1829
class TreeItem {
19-
element: HTMLTableCellElement; // The first TD
20-
contentElement: HTMLTableCellElement; // The second TD
30+
element: HTMLElement; // The DT element
31+
contentElement: HTMLElement; // The DD element
2132
isFolder: boolean; // Whether this item is a folder or a file
2233
isExpanded: boolean = true; // Whether the folder is expanded or not
23-
children: TableTree; // Children of this item, if it's a folder
34+
children: FileTree; // Children of this item, if it's a folder
2435
2536
toggleChildren(state: boolean | undefined) {
2637
if (state === undefined) state = !this.isExpanded;
2738
this.isExpanded = state;
28-
this.children.items.forEach((childData, childTr) => {
29-
childTr.classList.toggle('hidden', state);
39+
this.children.items.forEach((childData, childDiv) => {
40+
childDiv.classList.toggle('hidden', !state);
3041
childData.toggleChildren(state);
3142
});
43+
this.element.classList.toggle('closed', !state);
3244
}
3345
}
3446
35-
class TableTree {
36-
items: Map<HTMLTableRowElement, TreeItem> = new Map();
47+
class FileTree {
48+
items: Map<HTMLDivElement, TreeItem> = new Map();
3749
pathToItem: Map<string, TreeItem> = new Map();
3850
}
3951
40-
let tableTree = new TableTree();
52+
let fileTree = new FileTree();
4153
4254
onMounted(() => {
43-
// find all second TDs and add a class to them
44-
const tds = document.querySelectorAll('td:nth-child(2)');
45-
tds.forEach(td => {
46-
td.classList.add('hidden');
55+
// find all DD elements and add a class to them
56+
const dds = document.querySelectorAll('dd');
57+
dds.forEach(dd => {
58+
if (!props.showall)
59+
dd.classList.add('hidden');
4760
});
4861
4962
let firstItem: TreeItem | null = null;
5063
51-
// make all first TDs clickable
52-
const firstTds = document.querySelectorAll('td:first-child') as NodeListOf<HTMLTableCellElement>;
53-
firstTds.forEach(td => {
64+
// make all DT elements clickable
65+
const dts = document.querySelectorAll('dt') as NodeListOf<HTMLElement>;
66+
dts.forEach(dt => {
5467
5568
// If the text content looks like a path, split it by / and only keep the last part,
5669
// with a few tabs in front
5770
// If the last part is empty, it means this is a folder, show it with a folder icon
58-
let content = td.textContent?.trim() || '';
71+
let content = dt.textContent?.trim() || '';
5972
let isFolder = content.endsWith('/');
6073
61-
const parts = td.textContent?.split('/') || [];
74+
const parts = dt.textContent?.split('/') || [];
6275
const lastPartIndex = isFolder ? parts.length - 2 : parts.length - 1;
6376
const lastPart = parts[lastPartIndex] || ''; // Get the last part, or the second last if it's a folder
6477
65-
if (isFolder)
66-
td.classList.add('folder');
67-
else
68-
td.classList.add('file');
78+
if (isFolder) {
79+
dt.classList.add('folder');
80+
}
81+
else {
82+
dt.classList.add('file');
83+
const extension = lastPart.split('.').pop()?.toLowerCase() || '';
84+
dt.classList.add(extension);
85+
}
6986
70-
td.style.setProperty('--depth', `${lastPartIndex}`); // Set a custom property for depth
71-
td.textContent = lastPart; // Set the text content to the last part
87+
88+
dt.style.setProperty('--depth', `${lastPartIndex}`); // Set a custom property for depth
89+
dt.textContent = lastPart; // Set the text content to the last part
7290
7391
const path = parts.slice(0, lastPartIndex + 1).join('/');
74-
td.addEventListener('click', () => {
75-
const item = tableTree.pathToItem.get(path);
92+
if (!props.showall)
93+
dt.addEventListener('click', () => {
94+
const item = fileTree.pathToItem.get(path);
7695
if (isFolder && canToggleFolders) {
7796
if (item) {
7897
item.toggleChildren(!item.isExpanded);
7998
}
8099
}
81100
else {
82-
const nextTd = td.nextElementSibling as HTMLTableCellElement;
83-
const isOn = nextTd.classList.contains('hidden');
84-
nextTd.classList.toggle('hidden', !isOn);
101+
const nextDd = dt.nextElementSibling as HTMLElement;
102+
const isOn = nextDd.classList.contains('hidden');
103+
nextDd.classList.toggle('hidden', !isOn);
85104
if (selectedItem && selectedItem !== item) {
86105
selectedItem.contentElement.classList.toggle('hidden', isOn);
87106
selectedItem.element.classList.remove('selected');
@@ -91,78 +110,105 @@ onMounted(() => {
91110
}
92111
});
93112
94-
// Add the item to the table tree
95-
const tr = td.parentElement as HTMLTableRowElement;
96-
const contentTd = td.nextElementSibling as HTMLTableCellElement;
113+
// Add the item to the file tree
114+
const fileDiv = dt.parentElement as HTMLDivElement;
115+
const contentDd = dt.nextElementSibling as HTMLElement;
97116
98117
let newItem = new TreeItem();
99-
newItem.element = td;
100-
newItem.contentElement = contentTd;
118+
newItem.element = dt;
119+
newItem.contentElement = contentDd;
101120
newItem.isFolder = isFolder;
102-
newItem.children = new TableTree();
103-
121+
newItem.children = new FileTree();
122+
104123
if (firstItem === null)
105124
firstItem = newItem;
106125
107126
if (lastPartIndex == 0) {
108-
tableTree.items.set(tr, newItem);
127+
fileTree.items.set(fileDiv, newItem);
109128
}
110129
else {
111130
// Find the parent item based on the path
112131
const parentPath = parts.slice(0, lastPartIndex).join('/');
113132
// Add the item to the parent's children
114-
const parentData = tableTree.pathToItem.get(parentPath);
133+
const parentData = fileTree.pathToItem.get(parentPath);
115134
if (parentData) {
116-
parentData.children.items.set(tr, newItem);
135+
parentData.children.items.set(fileDiv, newItem);
136+
}
137+
else {
138+
// Traverse from the start and create the missing parents
139+
// TODO
140+
console.warn(`Parent not found for path: ${parentPath}. Creating implicit parents is not implemented yet.`);
117141
}
118142
}
119143
// Update the path to item map
120-
tableTree.pathToItem.set(path, newItem);
144+
fileTree.pathToItem.set(path, newItem);
121145
});
122146
123-
// The very first TD should be visible by default
124-
if (firstItem) {
147+
// The very first DT should be visible by default
148+
if (!props.showall && firstItem) {
125149
let item = firstItem as TreeItem;
126150
item.element.classList.remove('hidden');
127151
item.element.classList.add('selected'); // Highlight the first item
128-
const nextTd = item.contentElement;
129-
if (nextTd) {
130-
nextTd.classList.remove('hidden');
131-
selectedItem = item; // Set the selected TD to the first one
152+
const nextDd = item.contentElement;
153+
if (nextDd) {
154+
nextDd.classList.remove('hidden');
155+
selectedItem = item; // Set the selected DD to the first one
132156
}
133157
}
134158
});
135159
</script>
136160

137161
<style>
138162
139-
table {
163+
.file {
140164
position: relative;
165+
display: flex;
166+
align-items: stretch;
167+
}
168+
169+
div.header {
170+
border-bottom: 1px solid #eee;
141171
}
142172
143-
tr {
144-
border: none !important;
145-
width: 100%;
173+
span.header {
174+
width: 250px;
175+
font-weight: bold;
176+
display: inline-block;
177+
margin-bottom: 0.5em;
178+
margin-left: 0.1em;
146179
}
147180
148-
tr:hover {
181+
dt:hover {
149182
background-color: rgb(241, 241, 241) !important;
150183
}
151184
152-
html[data-theme='dark'] tr:hover {
185+
html[data-theme='dark'] dt:hover {
153186
background-color: rgb(50, 50, 50) !important;
154187
}
155188
156-
tr.hidden, td.hidden {
189+
.file.hidden, dt.hidden, dd.hidden {
157190
display: none;
158191
}
159192
160-
td {
193+
dl {
194+
overflow-x: auto;
195+
}
196+
197+
dd {
198+
min-width: 250px;
199+
}
200+
201+
dt, dd {
161202
padding-bottom: 0.3em !important;
162203
padding-top: 0.3em !important;
204+
margin: 0;
163205
}
164206
165-
td:first-child {
207+
dd > p {
208+
margin: 0;
209+
}
210+
211+
dt {
166212
--depth: 0;
167213
cursor: pointer;
168214
font-family: monospace;
@@ -171,24 +217,51 @@ td:first-child {
171217
padding-left: calc(var(--depth) * 1.5em) !important;
172218
width: calc(250px - var(--depth) * 1.5em);
173219
border-right: 1px solid #eee !important;
220+
flex-shrink: 0;
174221
175222
&.selected {
176223
font-weight: bold;
177224
}
178225
}
179226
180-
td.folder::before {
181-
content: '📁';
182-
margin-right: 1em;
227+
dt.folder {
228+
&::before {
229+
content: 'folder_open';
230+
font-family: 'Material Symbols Outlined';
231+
margin-right: 1em;
232+
}
233+
234+
&.closed::before {
235+
content: 'folder';
236+
}
183237
}
184238
185-
td.file::before {
186-
content: '📄';
187-
margin-right: 1em;
239+
dt.file {
240+
&::before {
241+
content: 'draft';
242+
font-family: 'Material Symbols Outlined';
243+
margin-right: 1em;
244+
}
245+
246+
&.js::before, &.ts::before, &.json::before {
247+
content: 'code';
248+
}
249+
250+
&.html::before {
251+
content: 'draft';
252+
}
253+
254+
&.css::before {
255+
content: 'code';
256+
}
257+
258+
&.glb::before {
259+
content: 'deployed_code';
260+
}
188261
}
189262
190-
td:nth-child(2) {
263+
dd {
191264
padding-left: 1em !important;
192-
width: initial;
265+
flex: 1;
193266
}
194267
</style>

0 commit comments

Comments
 (0)