Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active October 20, 2015 14:47
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/ce69c0cb655897b95daf to your computer and use it in GitHub Desktop.
Save nitaku/ce69c0cb655897b95daf to your computer and use it in GitHub Desktop.
Virtual Filesystem UI 2

An improvement upon the previous example. Files and folders are now sorted alphabetically, icons reflect the file's type, files are represented as big tiles with previews in case of image files.

# data
ramona = {id: 0, name: 'ramona flowers.png', type: 'image'}
bttf_cover = {id: 1, name: 'Back to the Future - Cover.jpg', type: 'image'}
photos = {
id: 10,
name:'Photos',
subfolders: [],
files: []
}
pictures = {
id: 2,
name:'Pictures',
subfolders: [photos],
files: [ramona, {id: 9, name: 'chibi miku.jpg', type: 'image'}, bttf_cover]
}
movies = {
id: 3,
name:'Movies',
subfolders: [],
files: [{id: 6, name: 'Back to the Future.mkv', type: 'video'}, bttf_cover]
}
documents = {
id: 4,
name:'Documents',
subfolders: [photos],
files: [ramona, {id: 12, name: 'money money money.txt', type: 'text'},{id: 13, name: 'pitfalls.pdf', type: 'pdf'}]
}
music = {
id: 14,
name:'Music',
subfolders: [],
files: [{id: 15, name: 'Tell your world (初音ミク).mp3', type: 'audio'}, {id: 16, name: 'Surrounded (Dream Theater).mp3', type: 'audio'}]
}
root = {
id: 5,
subfolders: [pictures, movies, documents, music],
files: [{id: 7, name: 'vmlinuz'}]
}
index_n_sort = (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.sort (a,b) -> d3.ascending(a.name, b.name)
node.files.sort (a,b) -> d3.ascending(a.name, b.name)
node.subfolders.forEach (sf) ->
index_n_sort(sf)
index_n_sort(root)
# status
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)
# UI
cwd = d3.select('#cwd')
subfolders_container = cwd.append('div')
files_container = cwd.append('div')
up = d3.select('#up_btn')
breadcrumb = d3.select('#breadcrumb')
up.on 'click', () -> cd__()
redraw = () ->
cwd_data = open_folders[open_folders.length-1]
subfolders = subfolders_container.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()
subfolders.order()
files = files_container.selectAll('.file')
.data(cwd_data.files, (d) -> d.id )
enter_files = files.enter().append('div')
.attr
class: 'file node'
enter_previews = enter_files.append('div')
.attr
class: 'preview'
enter_previews.each (d) ->
if d.type is 'image'
d3.select(this)
.style
'background-image': "url(#{encodeURI(d.name)})"
else
d3.select(this)
.html (d) -> "<i class='icon fa fa-4x fa-file#{if d.type? then '-'+d.type else ''}-o'></i>"
enter_files.append('div')
.attr
class: 'filename'
.html (d) -> "<span>#{d.name}</span>"
files.exit().remove()
files.order()
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 {
margin: 4px;
background: white;
}
.subfolder {
padding: 6px;
}
.subfolder:hover {
cursor: pointer;
background: #FFF4DC;
}
#cwd .icon {
color: #777;
}
.file {
display: inline-block;
width: 142px;
height: 132px;
text-align: center;
vertical-align: text-top;
position: relative;
}
.file .icon {
margin-top: 16px;
}
.preview {
width: 100%;
background-size: 100%;
height: 90px;
}
.filename {
box-sizing: border-box;
padding: 4px;
background: #EEE;
position: absolute;
bottom: 0;
width: 100%;
height: 42px;
line-height: 32px;
}
.filename > span {
padding: 0;
display: inline-block;
vertical-align: middle;
line-height: normal;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Virtual Filesystem UI 2</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, files_container, index_n_sort, movies, music, open_folders, photos, pictures, ramona, redraw, root, subfolders_container, up;
ramona = {
id: 0,
name: 'ramona flowers.png',
type: 'image'
};
bttf_cover = {
id: 1,
name: 'Back to the Future - Cover.jpg',
type: 'image'
};
photos = {
id: 10,
name: 'Photos',
subfolders: [],
files: []
};
pictures = {
id: 2,
name: 'Pictures',
subfolders: [photos],
files: [
ramona, {
id: 9,
name: 'chibi miku.jpg',
type: 'image'
}, bttf_cover
]
};
movies = {
id: 3,
name: 'Movies',
subfolders: [],
files: [
{
id: 6,
name: 'Back to the Future.mkv',
type: 'video'
}, bttf_cover
]
};
documents = {
id: 4,
name: 'Documents',
subfolders: [photos],
files: [
ramona, {
id: 12,
name: 'money money money.txt',
type: 'text'
}, {
id: 13,
name: 'pitfalls.pdf',
type: 'pdf'
}
]
};
music = {
id: 14,
name: 'Music',
subfolders: [],
files: [
{
id: 15,
name: 'Tell your world (初音ミク).mp3',
type: 'audio'
}, {
id: 16,
name: 'Surrounded (Dream Theater).mp3',
type: 'audio'
}
]
};
root = {
id: 5,
subfolders: [pictures, movies, documents, music],
files: [
{
id: 7,
name: 'vmlinuz'
}
]
};
index_n_sort = 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;
});
node.subfolders.sort(function(a, b) {
return d3.ascending(a.name, b.name);
});
node.files.sort(function(a, b) {
return d3.ascending(a.name, b.name);
});
return node.subfolders.forEach(function(sf) {
return index_n_sort(sf);
});
};
index_n_sort(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');
subfolders_container = cwd.append('div');
files_container = cwd.append('div');
up = d3.select('#up_btn');
breadcrumb = d3.select('#breadcrumb');
up.on('click', function() {
return cd__();
});
redraw = function() {
var cwd_data, enter_files, enter_previews, files, path_items, subfolders;
cwd_data = open_folders[open_folders.length - 1];
subfolders = subfolders_container.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();
subfolders.order();
files = files_container.selectAll('.file').data(cwd_data.files, function(d) {
return d.id;
});
enter_files = files.enter().append('div').attr({
"class": 'file node'
});
enter_previews = enter_files.append('div').attr({
"class": 'preview'
});
enter_previews.each(function(d) {
if (d.type === 'image') {
return d3.select(this).style({
'background-image': "url(" + (encodeURI(d.name)) + ")"
});
} else {
return d3.select(this).html(function(d) {
return "<i class='icon fa fa-4x fa-file" + (d.type != null ? '-' + d.type : '') + "-o'></i>";
});
}
});
enter_files.append('div').attr({
"class": 'filename'
}).html(function(d) {
return "<span>" + d.name + "</span>";
});
files.exit().remove();
files.order();
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