A simple User Interface for a virtual filesystem (a la Google Drive). Folders and files can be in more than one folder (e.g. Home > Pictures > goofy.png
is also in Home > Documents
).
Last active
October 20, 2015 14:46
-
-
Save nitaku/5952d82b1846525207f5 to your computer and use it in GitHub Desktop.
Virtual Filesystem UI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
goofy = {id: 0, name: 'goofy.png'} | |
bttf_cover = {id: 1, name: 'Back to the Future (Cover).jpg'} | |
photos = { | |
id: 10, | |
name:'Photos', | |
subfolders: [], | |
files: [{id: 11, name: 'me.jpg'}] | |
} | |
pictures = { | |
id: 2, | |
name:'Pictures', | |
subfolders: [photos], | |
files: [goofy, {id: 8, name: 'mickey.png'}, {id: 9, name: 'donald.png'}, bttf_cover] | |
} | |
movies = { | |
id: 3, | |
name:'Movies', | |
subfolders: [], | |
files: [{id: 6, name: 'Back to the Future.mkv'}, bttf_cover] | |
} | |
documents = { | |
id: 4, | |
name:'Documents', | |
subfolders: [photos], | |
files: [goofy] | |
} | |
root = { | |
id: 5, | |
subfolders: [pictures, movies, documents], | |
files: [{id: 7, name: 'vmlinuz'}] | |
} | |
index = (node) -> | |
if node.index? | |
return | |
node.index = {} | |
node.subfolders.forEach (sf) -> node.index[sf.name] = sf | |
node.files.forEach (f) -> node.index[f.name] = f | |
node.subfolders.forEach (sf) -> | |
index(sf) | |
index(root) | |
# walk = (node, spath) -> | |
# if spath.length is 0 | |
# return node | |
# return walk(node.index[spath[0]], spath[1..]) | |
# get_node = (path) -> | |
# if path[path.length-1] is '/' | |
# path = path[...path.length-1] | |
# spath = path.split('/') | |
# console.log walk(root, spath[1..]) | |
open_folders = [root] | |
cd = (sf_name) -> | |
open_folders.push open_folders[open_folders.length-1].index[sf_name] | |
redraw() | |
cd__ = () -> | |
if open_folders.length > 1 | |
open_folders.pop() | |
redraw() | |
cut_path = (folder) -> | |
if open_folders.length is 1 or open_folders[open_folders.length-1].id is folder.id | |
redraw() | |
return | |
open_folders.pop() | |
cut_path(folder) | |
# ls = () -> | |
# open_folders[open_folders.length-1].subfolders.forEach (sf) -> | |
# console.log '['+sf.name+']' | |
# open_folders[open_folders.length-1].files.forEach (f) -> | |
# console.log f.name | |
# pwd = () -> | |
# console.log open_folders.map((f) -> if f.name? then f.name else '').join('/') + '/' | |
cwd = d3.select('#cwd') | |
up = d3.select('#up_btn') | |
breadcrumb = d3.select('#breadcrumb') | |
up.on 'click', () -> cd__() | |
redraw = () -> | |
cwd_data = open_folders[open_folders.length-1] | |
subfolders = cwd.selectAll('.subfolder') | |
.data(cwd_data.subfolders, (d) -> d.id ) | |
subfolders.enter().append('div') | |
.attr | |
class: 'subfolder node' | |
.html (d) -> '<i class="icon fa fa-fw fa-folder"></i> '+d.name | |
.on 'click', (d) -> cd d.name | |
subfolders.exit().remove() | |
files = cwd.selectAll('.file') | |
.data(cwd_data.files, (d) -> d.id ) | |
files.enter().append('div') | |
.attr | |
class: 'file node' | |
.html (d) -> '<i class="icon fa fa-fw fa-file-o"></i> '+d.name | |
files.exit().remove() | |
path_items = breadcrumb.selectAll('.path_item') | |
.data(open_folders, (d) -> d.id ) | |
path_items.enter().append('div') | |
.attr | |
class: 'path_item' | |
.html (d) -> '<span>' + if d.name? then d.name else 'Home' + '</span>' | |
.on 'click', (d) -> cut_path(d) | |
path_items.exit().remove() | |
redraw() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body { | |
margin: 0; | |
padding: 0; | |
font-family: sans-serif; | |
font-size: 14px; | |
color: #333; | |
background: #DDD; | |
} | |
#cwd { | |
padding: 4px; | |
} | |
#bar { | |
padding: 6px; | |
box-shadow: 0px 0px 6px #777; | |
background: #EEE; | |
} | |
#bar > * { | |
display: inline-block; | |
} | |
.path_item { | |
display: inline-block; | |
margin-right: 6px; | |
cursor: pointer; | |
} | |
.path_item:not(:first-child)::before { | |
content: '\f054'; | |
margin-right: 6px; | |
font-family: 'FontAwesome'; | |
color: #999; | |
font-size: 10px; | |
} | |
.path_item:hover > span { | |
text-decoration: underline; | |
} | |
.node { | |
padding: 6px; | |
margin: 4px; | |
background: white; | |
} | |
.subfolder:hover { | |
cursor: pointer; | |
background: #FFF4DC; | |
} | |
#cwd .icon { | |
color: #777; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Virtual Filesystem UI</title> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<link rel="stylesheet" href="index.css"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css"> | |
</head> | |
<body> | |
<div id="bar"> | |
<button id="up_btn"><i class="icon fa fa-level-up"></i></button> | |
<div id="breadcrumb"></div> | |
</div> | |
<div id="cwd"></div> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.10.0 | |
(function() { | |
var breadcrumb, bttf_cover, cd, cd__, cut_path, cwd, documents, goofy, index, movies, open_folders, photos, pictures, redraw, root, up; | |
goofy = { | |
id: 0, | |
name: 'goofy.png' | |
}; | |
bttf_cover = { | |
id: 1, | |
name: 'Back to the Future (Cover).jpg' | |
}; | |
photos = { | |
id: 10, | |
name: 'Photos', | |
subfolders: [], | |
files: [ | |
{ | |
id: 11, | |
name: 'me.jpg' | |
} | |
] | |
}; | |
pictures = { | |
id: 2, | |
name: 'Pictures', | |
subfolders: [photos], | |
files: [ | |
goofy, { | |
id: 8, | |
name: 'mickey.png' | |
}, { | |
id: 9, | |
name: 'donald.png' | |
}, bttf_cover | |
] | |
}; | |
movies = { | |
id: 3, | |
name: 'Movies', | |
subfolders: [], | |
files: [ | |
{ | |
id: 6, | |
name: 'Back to the Future.mkv' | |
}, bttf_cover | |
] | |
}; | |
documents = { | |
id: 4, | |
name: 'Documents', | |
subfolders: [photos], | |
files: [goofy] | |
}; | |
root = { | |
id: 5, | |
subfolders: [pictures, movies, documents], | |
files: [ | |
{ | |
id: 7, | |
name: 'vmlinuz' | |
} | |
] | |
}; | |
index = function(node) { | |
if (node.index != null) { | |
return; | |
} | |
node.index = {}; | |
node.subfolders.forEach(function(sf) { | |
return node.index[sf.name] = sf; | |
}); | |
node.files.forEach(function(f) { | |
return node.index[f.name] = f; | |
}); | |
return node.subfolders.forEach(function(sf) { | |
return index(sf); | |
}); | |
}; | |
index(root); | |
open_folders = [root]; | |
cd = function(sf_name) { | |
open_folders.push(open_folders[open_folders.length - 1].index[sf_name]); | |
return redraw(); | |
}; | |
cd__ = function() { | |
if (open_folders.length > 1) { | |
open_folders.pop(); | |
return redraw(); | |
} | |
}; | |
cut_path = function(folder) { | |
if (open_folders.length === 1 || open_folders[open_folders.length - 1].id === folder.id) { | |
redraw(); | |
return; | |
} | |
open_folders.pop(); | |
return cut_path(folder); | |
}; | |
cwd = d3.select('#cwd'); | |
up = d3.select('#up_btn'); | |
breadcrumb = d3.select('#breadcrumb'); | |
up.on('click', function() { | |
return cd__(); | |
}); | |
redraw = function() { | |
var cwd_data, files, path_items, subfolders; | |
cwd_data = open_folders[open_folders.length - 1]; | |
subfolders = cwd.selectAll('.subfolder').data(cwd_data.subfolders, function(d) { | |
return d.id; | |
}); | |
subfolders.enter().append('div').attr({ | |
"class": 'subfolder node' | |
}).html(function(d) { | |
return '<i class="icon fa fa-fw fa-folder"></i> ' + d.name; | |
}).on('click', function(d) { | |
return cd(d.name); | |
}); | |
subfolders.exit().remove(); | |
files = cwd.selectAll('.file').data(cwd_data.files, function(d) { | |
return d.id; | |
}); | |
files.enter().append('div').attr({ | |
"class": 'file node' | |
}).html(function(d) { | |
return '<i class="icon fa fa-fw fa-file-o"></i> ' + d.name; | |
}); | |
files.exit().remove(); | |
path_items = breadcrumb.selectAll('.path_item').data(open_folders, function(d) { | |
return d.id; | |
}); | |
path_items.enter().append('div').attr({ | |
"class": 'path_item' | |
}).html(function(d) { | |
return '<span>' + (d.name != null ? d.name : 'Home' + '</span>'); | |
}).on('click', function(d) { | |
return cut_path(d); | |
}); | |
return path_items.exit().remove(); | |
}; | |
redraw(); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment