1
0
Fork 0

Upgrade to Bootstrap 5, and improve UI

pull/17/head
Ambrose Chua 2021-05-07 20:57:15 +08:00
parent 38de494408
commit 36cdb9aa8b
26 changed files with 509 additions and 463 deletions

View File

@ -1,6 +0,0 @@
body {
font-family: -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif;
}

View File

@ -2,3 +2,22 @@
overflow: hidden; overflow: hidden;
word-wrap: break-word; word-wrap: break-word;
} }
.badge-alignment {
margin-top: 0.25em;
}
.stretched-invisible-label {
display: block;
}
.stretched-invisible-label > * {
position: relative;
z-index: 1;
}
.stretched-invisible-label::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
}

View File

@ -1,5 +0,0 @@
/* jshint esversion: 6 */
$(document).ready(() => {
$("[title]").tooltip();
});

View File

@ -1,8 +1,7 @@
/* jshint esversion: 6 */ /* jshint esversion: 6 */
$(document).ready(() => { let $inputs = $(".input-group-digits");
let $inputs = $(".input-group-digits"); $inputs.each((i, input) => {
$inputs.each((i, input) => {
let cleanup = () => { let cleanup = () => {
$(input).find("input").each((i, ele) => { $(input).find("input").each((i, ele) => {
let cleaned = $(ele).val().replace(/[^0-9]/, ""); let cleaned = $(ele).val().replace(/[^0-9]/, "");
@ -49,5 +48,4 @@ $(document).ready(() => {
$digits = $(input).find("input"); $digits = $(input).find("input");
$digits.on("keyup", update); $digits.on("keyup", update);
$digits.on("change", update); $digits.on("change", update);
});
}); });

View File

@ -1,30 +1,51 @@
/* jshint esversion: 6 */ /* jshint esversion: 6 */
$(document).ready(() => { function htmlEscape(text) {
let $select = $(".multi-select"); const p = document.createElement('p');
p.innerText = text;
return p.innerHTML;
}
let setSelected = (files) => { let $select = $(".multi-select");
let setSelected = (files) => {
$(".multi-files-value").val(JSON.stringify(files.map(f => f.name))); $(".multi-files-value").val(JSON.stringify(files.map(f => f.name)));
if (files.length == 0) {
$(".multi-files").html(`<li class="list-group-item text-muted">No files selected</li>`);
return
}
$(".multi-files").html( $(".multi-files").html(
files.map(f => { files.map(f => {
return `<li class="list-group-item d-flex align-items-start justify-content-between"><span class="name">${f.name}</span> <span class="badge badge-pill badge-secondary">${f.size}</span></li>`; return `
<li class="list-group-item d-flex align-items-start justify-content-between">
<span class="name">${htmlEscape(f.name)}</span>
${f.type == "directory" ? `` : `<span class="badge rounded-pill bg-secondary badge-alignment">${filesize(f.size)}</span>`}
</li>
`;
}).join("") }).join("")
); );
}; const hasDirectory = files.reduce((a, f) => a || f.type == "directory", false);
const totalSize = files.map(f => f.size).reduce((a, b) => a + b);
if (hasDirectory) {
$(".multi-files-total").text("Unknown");
} else {
$(".multi-files-total").text(filesize(totalSize));
}
};
let updateSelected = () => { const updateSelected = () => {
let $selected = $(".multi-select:checked"); let $selected = $(".multi-select:checked");
let files = []; let files = [];
$selected.each((i, ele) => { $selected.each((i, ele) => {
files.push({ files.push({
name: $(ele).data("select"), name: $(ele).data("select"),
type: $(ele).data("select-type"),
size: $(ele).data("select-size") size: $(ele).data("select-size")
}); });
}); });
setSelected(files); setSelected(files);
} }
$select.on("change", updateSelected); $select.on("change", updateSelected);
updateSelected(); updateSelected();
});

6
assets/navbar.css Normal file
View File

@ -0,0 +1,6 @@
#navbar {
overflow-x: auto;
}
.nav-link {
white-space: nowrap;
}

View File

@ -1,11 +1,9 @@
/* jshint esversion: 6 */ /* jshint esversion: 6 */
$(document).ready(() => { const $shell = $("#shell");
let $shell = $("#shell"); const $close = $("#shell-close");
if ($shell.length < 1) {
return; if ($shell.length > 0) {
}
let $close = $("#shell-close");
const ws = new WebSocket("ws" + (window.location.protocol === "https:" ? "s" : "") + "://" + window.location.host + "/websocket?path=" + encodeURIComponent($shell.data("path"))); const ws = new WebSocket("ws" + (window.location.protocol === "https:" ? "s" : "") + "://" + window.location.host + "/websocket?path=" + encodeURIComponent($shell.data("path")));
@ -54,4 +52,5 @@ $(document).ready(() => {
}); });
}); });
});
}

5
assets/tooltip.js Normal file
View File

@ -0,0 +1,5 @@
/* jshint esversion: 6 */
document.querySelectorAll("[title]").forEach(element => {
new bootstrap.Tooltip(element);
});

View File

@ -1,24 +1,22 @@
/* jshint esversion: 6 */ /* jshint esversion: 6 */
$(document).ready(() => { const $form = $("form[action='@upload']");
let $form = $("form[action='@upload']"); const $file = $("#upload-file");
let $file = $("#upload-file");
$(".upload-unhide").fadeOut(); $(".upload-unhide").fadeOut();
$file.on("change", () => { $file.on("change", () => {
let file = $file[0].files[0]; const file = $file[0].files[0];
let fnElement = $file.parent().find(".custom-file-label"); const fnElement = $file.parent().find(".custom-file-label");
fnElement.addClass("file-selected"); fnElement.addClass("file-selected");
fnElement.text(file.name); fnElement.text(file.name);
$form.find("#upload-file-size").val(filesize(file.size)); $form.find("#upload-file-size").val(filesize(file.size));
$form.find("[name=saveas]").val(file.name); $form.find("[name=saveas]").val(file.name);
$(".upload-unhide").fadeIn(); $(".upload-unhide").fadeIn();
}); });
$form.on("submit", () => { $form.on("submit", () => {
let putresource = $form.find("[name=saveas]").val(); let putresource = $form.find("[name=saveas]").val();
// TODO: do XHR to PUT at putresource // TODO: do XHR to PUT at putresource
});
}); });

View File

@ -34,6 +34,12 @@ app.engine("handlebars", hbs({
layoutsDir: path.join(__dirname, "views", "layouts"), layoutsDir: path.join(__dirname, "views", "layouts"),
defaultLayout: "main", defaultLayout: "main",
helpers: { helpers: {
either: (a, b, options) => {
if (a || b) {
return options.fn();
}
},
filesize: filesize,
octicon: (i, options) => { octicon: (i, options) => {
if (!octicons[i]) { if (!octicons[i]) {
return new handlebars.SafeString(octicons.question.toSVG()); return new handlebars.SafeString(octicons.question.toSVG());
@ -174,7 +180,7 @@ app.put("/*", (req, res) => {
res.redirect("back"); res.redirect("back");
}); });
save.on("error", (err) => { save.on("error", (err) => {
res.flash("error", err); res.flash("error", err.toString());
res.redirect("back"); res.redirect("back");
}); });
} }
@ -230,9 +236,13 @@ app.post("/*@upload", (req, res) => {
req.flash("error", "File exists, cannot overwrite. "); req.flash("error", "File exists, cannot overwrite. ");
res.redirect("back"); res.redirect("back");
}).catch((err) => { }).catch((err) => {
console.log("saving"); const saveName = relative(res.filename, saveas);
let save = fs.createWriteStream(relative(res.filename, saveas)); console.log("saving file to " + saveName);
let save = fs.createWriteStream(saveName);
save.on("close", () => { save.on("close", () => {
if (res.headersSent) {
return;
}
if (buff.length === 0) { if (buff.length === 0) {
req.flash("success", "File saved. Warning: empty file."); req.flash("success", "File saved. Warning: empty file.");
} }
@ -243,7 +253,7 @@ app.post("/*@upload", (req, res) => {
res.redirect("back"); res.redirect("back");
}); });
save.on("error", (err) => { save.on("error", (err) => {
req.flash("error", err); req.flash("error", err.toString());
res.redirect("back"); res.redirect("back");
}); });
save.write(buff); save.write(buff);
@ -277,7 +287,7 @@ app.post("/*@mkdir", (req, res) => {
}).catch((err) => { }).catch((err) => {
fs.mkdir(relative(res.filename, folder), (err) => { fs.mkdir(relative(res.filename, folder), (err) => {
if (err) { if (err) {
req.flash("error", err); req.flash("error", err.toString());
res.redirect("back"); res.redirect("back");
return; return;
} }
@ -341,7 +351,7 @@ app.post("/*@delete", (req, res) => {
res.redirect("back"); res.redirect("back");
}); });
}).catch((err) => { }).catch((err) => {
req.flash("error", err); req.flash("error", err.toString());
res.redirect("back"); res.redirect("back");
}); });
}); });
@ -394,7 +404,7 @@ app.get("/*@download", (req, res) => {
zip.finalize(); zip.finalize();
}).catch((err) => { }).catch((err) => {
console.log(err); console.log(err);
req.flash("error", err); req.flash("error", err.toString());
res.redirect("back"); res.redirect("back");
}); });
}); });
@ -515,7 +525,7 @@ app.get("/*", (req, res) => {
resolve({ resolve({
name: f, name: f,
isdirectory: stats.isDirectory(), isdirectory: stats.isDirectory(),
size: filesize(stats.size) size: stats.size
}); });
}); });
}); });
@ -551,6 +561,9 @@ app.get("/*", (req, res) => {
} }
else if (res.stats.isFile()) { else if (res.stats.isFile()) {
res.sendFile(relative(res.filename), { res.sendFile(relative(res.filename), {
headers: {
"Content-Security-Policy": "default-src 'self'; script-src 'none'; sandbox"
},
dotfiles: "allow" dotfiles: "allow"
}); });
} }

50
package-lock.json generated
View File

@ -10,7 +10,7 @@
"dependencies": { "dependencies": {
"archiver": "^5.3.0", "archiver": "^5.3.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"bootstrap": "^4.6.0", "bootstrap": "^5.0.0",
"connect-busboy": "^0.0.2", "connect-busboy": "^0.0.2",
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"express": "^4.17.1", "express": "^4.17.1",
@ -32,6 +32,16 @@
"file-manager": "index.js" "file-manager": "index.js"
} }
}, },
"node_modules/@popperjs/core": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz",
"integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.7", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@ -168,16 +178,15 @@
} }
}, },
"node_modules/bootstrap": { "node_modules/bootstrap": {
"version": "4.6.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz",
"integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==", "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/bootstrap" "url": "https://opencollective.com/bootstrap"
}, },
"peerDependencies": { "peerDependencies": {
"jquery": "1.9.1 - 3", "@popperjs/core": "^2.9.2"
"popper.js": "^1.16.1"
} }
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
@ -914,17 +923,6 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
}, },
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/printj": { "node_modules/printj": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
@ -1295,6 +1293,12 @@
} }
}, },
"dependencies": { "dependencies": {
"@popperjs/core": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz",
"integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==",
"peer": true
},
"accepts": { "accepts": {
"version": "1.3.7", "version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@ -1407,9 +1411,9 @@
} }
}, },
"bootstrap": { "bootstrap": {
"version": "4.6.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0.tgz",
"integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==", "integrity": "sha512-tmhPET9B9qCl8dCofvHeiIhi49iBt0EehmIsziZib65k1erBW1rHhj2s/2JsuQh5Pq+xz2E9bEbzp9B7xHG+VA==",
"requires": {} "requires": {}
}, },
"brace-expansion": { "brace-expansion": {
@ -1976,12 +1980,6 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
}, },
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"peer": true
},
"printj": { "printj": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",

View File

@ -8,7 +8,7 @@
"dependencies": { "dependencies": {
"archiver": "^5.3.0", "archiver": "^5.3.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"bootstrap": "^4.6.0", "bootstrap": "^5.0.0",
"connect-busboy": "^0.0.2", "connect-busboy": "^0.0.2",
"connect-flash": "^0.1.1", "connect-flash": "^0.1.1",
"express": "^4.17.1", "express": "^4.17.1",

View File

@ -4,35 +4,37 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>File Manager</title> <title>File Manager</title>
<!-- icons -->
<link rel="stylesheet" href="/@assets/octicons/build.css" /> <link rel="stylesheet" href="/@assets/octicons/build.css" />
<!-- bootstrap -->
<script src="/@assets/jquery/jquery.min.js"></script> <script src="/@assets/jquery/jquery.min.js" defer></script>
<link rel="stylesheet" href="/@assets/bootstrap/css/bootstrap.min.css" /> <link rel="stylesheet" href="/@assets/bootstrap/css/bootstrap.min.css" />
<script src="/@assets/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="/@assets/bootstrap/js/bootstrap.bundle.min.js" defer></script>
<!-- xterm -->
<link rel="stylesheet" href="/@assets/xterm/css/xterm.css" /> <link rel="stylesheet" href="/@assets/xterm/css/xterm.css" />
<script src="/@assets/xterm/lib/xterm.js"></script> <script src="/@assets/xterm/lib/xterm.js" defer></script>
<script src="/@assets/xterm-addon-attach/lib/xterm-addon-attach.js"></script> <script src="/@assets/xterm-addon-attach/lib/xterm-addon-attach.js" defer></script>
<script src="/@assets/xterm-addon-fit/lib/xterm-addon-fit.js"></script> <script src="/@assets/xterm-addon-fit/lib/xterm-addon-fit.js" defer></script>
<!-- file size math -->
<script src="/@assets/filesize/filesize.js"></script> <script src="/@assets/filesize/filesize.js" defer></script>
<!-- navbar, tooltip -->
<link rel="stylesheet" href="/@assets/fonts.css" /> <link rel="stylesheet" href="/@assets/navbar.css" />
<script src="/@assets/tooltip.js" defer></script>
<!-- list, multi-select -->
<link rel="stylesheet" href="/@assets/list.css" /> <link rel="stylesheet" href="/@assets/list.css" />
<script src="/@assets/list.js"></script> <script src="/@assets/multi.js" defer></script>
<script src="/@assets/multi.js"></script> <!-- upload -->
<link rel="stylesheet" href="/@assets/upload.css" /> <link rel="stylesheet" href="/@assets/upload.css" />
<script src="/@assets/upload.js"></script> <script src="/@assets/upload.js" defer></script>
<!-- login -->
<link rel="stylesheet" href="/@assets/login.css" /> <link rel="stylesheet" href="/@assets/login.css" />
<script src="/@assets/login.js"></script> <script src="/@assets/login.js" defer></script>
<!-- cmd -->
<link rel="stylesheet" href="/@assets/cmd.css" /> <link rel="stylesheet" href="/@assets/cmd.css" />
<script src="/@assets/shell.js"></script> <!-- shell -->
<script src="/@assets/shell.js" defer></script>
</head> </head>
<body> <body class="bg-light">
{{{body}}} {{{body}}}
</body> </body>
</html> </html>

View File

@ -15,17 +15,19 @@
<ul class="list-group"> <ul class="list-group">
{{#each files}} {{#each files}}
<li class="list-group-item"> <li class="list-group-item">
<div class="custom-control custom-checkbox"> <label for="check{{@index}}" class="stretched-invisible-label">
<input type="checkbox" class="custom-control-input multi-select" data-select="{{name}}" data-select-size="{{size}}" id="check{{@index}}"> <div class="form-check">
<label class="custom-control-label d-flex align-items-start justify-content-between" for="check{{@index}}"> <input type="checkbox" class="form-check-input multi-select" data-select="{{name}}" data-select-size="{{size}}" data-select-type="{{#if isdirectory}}directory{{else}}file{{/if}}" id="check{{@index}}">
<span class="form-check-label d-flex align-items-start justify-content-between">
{{#if isdirectory}} {{#if isdirectory}}
<a href="./{{name}}/" class="name">{{name}}/</a> <a href="./{{name}}/" class="name">{{name}}/</a>
{{else}} {{else}}
<a href="./{{name}}" class="name mr-auto">{{name}}</a> <a href="./{{name}}" class="name">{{name}}</a>
<span class="badge badge-pill badge-secondary">{{size}}</span> <span class="badge rounded-pill bg-secondary badge-alignment">{{filesize size}}</span>
{{/if}} {{/if}}
</label> </span>
</div> </div>
</label>
</li> </li>
{{else}} {{else}}
<li class="list-group-item"> <li class="list-group-item">

View File

@ -1,12 +1,10 @@
<form action="@cmd" method="post"> <form action="@cmd" method="post">
<div id="cmd" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div id="cmd" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-dialog-centered modal-sm">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-light">
<h5 class="modal-title">Run command</h5> <h5 class="modal-title">Run command</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
@ -14,8 +12,8 @@
<input name="cmd" class="form-control" type="text" id="cmd-cmd" placeholder="g++ sort.c" required /> <input name="cmd" class="form-control" type="text" id="cmd-cmd" placeholder="g++ sort.c" required />
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer bg-light">
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Run</button> <button type="submit" class="btn btn-primary">Run</button>
</div> </div>
</div> </div>

View File

@ -1,12 +1,10 @@
<form action="@delete" method="post"> <form action="@delete" method="post">
<div id="delete" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div id="delete" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-light">
<h5 class="modal-title">Are you sure?</h5> <h5 class="modal-title">Are you sure?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>You are deleting the following files: </p> <p>You are deleting the following files: </p>
@ -15,8 +13,8 @@
</ul> </ul>
<input type="hidden" name="files" value="" class="multi-files-value" /> <input type="hidden" name="files" value="" class="multi-files-value" />
</div> </div>
<div class="modal-footer"> <div class="modal-footer bg-light">
<button type="reset" class="btn btn-primary" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-primary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger">Delete</button> <button type="submit" class="btn btn-danger">Delete</button>
</div> </div>
</div> </div>

View File

@ -1,22 +1,21 @@
<form action="@download" method="get"> <form action="@download" method="get">
<div id="download" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div id="download" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-light">
<h5 class="modal-title">Download file archive?</h5> <h5 class="modal-title">Download file archive?</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>Your archive will contain the following files: </p> <p>Your archive will contain the following files: </p>
<ul class="list-group multi-files"> <ul class="list-group multi-files">
</ul> </ul>
<p class="mt-3 mb-0">Total size: <span class="multi-files-total">Unknown</span></p>
<input type="hidden" name="files" value="" class="multi-files-value" /> <input type="hidden" name="files" value="" class="multi-files-value" />
</div> </div>
<div class="modal-footer"> <div class="modal-footer bg-light">
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Download</button> <button type="submit" class="btn btn-primary">Download</button>
</div> </div>
</div> </div>

View File

@ -1,21 +1,19 @@
<form action="@mkdir" method="post"> <form action="@mkdir" method="post">
<div id="mkdir" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div id="mkdir" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-light">
<h5 class="modal-title">Create a folder</h5> <h5 class="modal-title">Create a folder</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div>
<label for="mkdir-folder">Folder name: </label> <label class="form-label" for="mkdir-folder">Folder name</label>
<input name="folder" class="form-control" type="text" id="mkdir-folder" placeholder="folder" required /> <input class="form-control" name="folder" type="text" id="mkdir-folder" placeholder="folder" required />
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer bg-light">
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Create</button> <button type="submit" class="btn btn-primary">Create</button>
</div> </div>
</div> </div>

View File

@ -1,31 +1,24 @@
<form action="@upload" method="post" enctype="multipart/form-data"> <form action="@upload" method="post" enctype="multipart/form-data">
<div id="upload" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div id="upload" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-sm"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-light">
<h5 class="modal-title">Upload a file</h5> <h5 class="modal-title">Upload a file</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <input class="form-control" type="file" id="upload-file" name="file">
<div class="custom-file"> <div class="mt-3 upload-unhide">
<input name="file" type="file" id="upload-file" class="custom-file-input"> <label class="form-label" for="upload-file-size">Filesize</label>
<label class="custom-file-label" for="upload-file">Choose file</label>
</div>
</div>
<div class="form-group upload-unhide">
<label for="upload-file-size">Filesize: </label>
<input class="form-control" type="text" disabled id="upload-file-size" placeholder="0B" /> <input class="form-control" type="text" disabled id="upload-file-size" placeholder="0B" />
</div> </div>
<div class="form-group upload-unhide"> <div class="mt-3 upload-unhide">
<label for="upload-file-saveas">Save as: </label> <label class="form-label" for="upload-file-saveas">Save as</label>
<input name="saveas" class="form-control" type="text" id="upload-file-saveas" placeholder="filename" /> <input class="form-control" type="text" name="saveas" id="upload-file-saveas" placeholder="filename" />
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer bg-light">
<button type="reset" class="btn btn-secondary" data-dismiss="modal">Cancel</button> <button type="reset" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Upload</button> <button type="submit" class="btn btn-primary">Upload</button>
</div> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
<nav class="navbar navbar-dark bg-primary fixed-top navbar-expand-sm"> <nav class="navbar navbar-dark bg-primary fixed-top navbar-expand-sm">
<div class="container-fluid">
<a class="navbar-brand" href="/">File Manager</a> <a class="navbar-brand" href="/">File Manager</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbar"> <div class="collapse navbar-collapse" id="navbar">
@ -23,4 +24,5 @@
{{/if}} {{/if}}
</div> </div>
</div> </div>
</div>
</nav> </nav>

View File

@ -1,17 +1,20 @@
<nav class="navbar navbar-light bg-light fixed-bottom p-1 px-2"> <nav class="navbar navbar-light bg-light fixed-bottom">
<div class="btn-group m-1" role="group"> <div class="container-fluid px-2">
<a class="btn btn-primary" href="@upload" data-toggle="modal" data-target="#upload" data-placement="top" title="Upload"> <div class="d-flex">
<div class="btn-group me-1" role="group">
<a class="btn btn-primary" href="@upload" data-bs-toggle="modal" data-bs-target="#upload" data-placement="top" title="Upload">
{{octicon "cloud-upload"}} {{octicon "cloud-upload"}}
<span class="d-none d-sm-inline">Upload</span> <span class="d-none d-sm-inline">Upload</span>
</a> </a>
<a class="btn btn-secondary" href="@mkdir" data-toggle="modal" data-target="#mkdir" data-placement="top" title="New folder"> <a class="btn btn-secondary" href="@mkdir" data-bs-toggle="modal" data-bs-target="#mkdir" data-placement="top" title="New folder">
{{octicon "file-directory"}} {{octicon "file-directory"}}
<span class="d-none d-md-inline">New folder</span> <span class="d-none d-md-inline">New folder</span>
</a> </a>
</div> </div>
<div class="btn-group m-1" role="group"> {{#either cmdable shellable}}
<div class="btn-group me-1" role="group">
{{#if cmdable}} {{#if cmdable}}
<a class="btn btn-info" href="@cmd" data-toggle="modal" data-target="#cmd" data-placement="top" title="Run command"> <a class="btn btn-info" href="@cmd" data-bs-toggle="modal" data-bs-target="#cmd" data-placement="top" title="Run command">
{{octicon "terminal"}} {{octicon "terminal"}}
<span class="d-none d-lg-inline">Run command</span> <span class="d-none d-lg-inline">Run command</span>
</a> </a>
@ -23,22 +26,27 @@
</a> </a>
{{/if}} {{/if}}
</div> </div>
<div class="btn-group m-1" role="group"> {{/either}}
<a class="btn btn-success" href="@download" data-toggle="modal" data-target="#download" data-placement="top" title="Download files as zip"> <div class="btn-group me-1" role="group">
<a class="btn btn-success" href="@download" data-bs-toggle="modal" data-bs-target="#download" data-placement="top" title="Download files as zip">
{{octicon "file-zip"}} {{octicon "file-zip"}}
<span class="d-none d-md-inline">Download</span> <span class="d-none d-md-inline">Download</span>
</a> </a>
</div> </div>
<div class="btn-group m-1" role="group"> <div class="btn-group me-1" role="group">
<a class="btn btn-danger" href="@delete" data-toggle="modal" data-target="#delete" data-placement="top" title="Delete files"> <a class="btn btn-danger" href="@delete" data-bs-toggle="modal" data-bs-target="#delete" data-placement="top" title="Delete files">
{{octicon "trashcan"}} {{octicon "trashcan"}}
<span class="d-none d-md-inline">Delete</span> <span class="d-none d-md-inline">Delete</span>
</a> </a>
</div> </div>
<div class="btn-group m-1 ml-auto" role="group"> </div>
<div class="d-flex">
<div class="btn-group ml-1" role="group">
<a class="btn btn-warning" href="./" data-placement="top" title="Refresh list"> <a class="btn btn-warning" href="./" data-placement="top" title="Refresh list">
{{octicon "sync"}} {{octicon "sync"}}
<span class="d-none d-lg-inline">Refresh</span> <span class="d-none d-lg-inline">Refresh</span>
</a> </a>
</div> </div>
</div>
</div>
</nav> </nav>