Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active October 20, 2015 14:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/5952d82b1846525207f5 to your computer and use it in GitHub Desktop.
Save nitaku/5952d82b1846525207f5 to your computer and use it in GitHub Desktop.
Virtual Filesystem UI

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).

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()
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;
}
<!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>
// 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