diff --git a/_embed/public/css/styles.css b/_embed/public/css/styles.css index 2a4fd88b..d87651dc 100644 --- a/_embed/public/css/styles.css +++ b/_embed/public/css/styles.css @@ -53,6 +53,10 @@ button:hover { background-color: #1E88E5; } +.mobile-only { + display: none !important; +} + .container { width: 95%; max-width: 960px; @@ -366,6 +370,7 @@ header>div { display: flex; width: 100%; padding: 0.5em 0.5em 0.5em 1em; + align-items: center; } header p { @@ -387,12 +392,12 @@ header>div div { vertical-align: middle; position: relative; text-overflow: ellipsis; - overflow: hidden; + /* overflow: hidden; */ white-space: nowrap; } header .actions { - margin-left: auto; + /* margin-left: auto; */ } #logout { @@ -401,6 +406,20 @@ header .actions { padding: .15em; } +#click-overlay { + display: none; + position: fixed; + cursor: pointer; + top: 0; + left: 0; + height: 100%; + width: 100%; +} + +#click-overlay.active { + display: block; +} + /* * * * * * * * * * * * * * * * * TOP BAR * @@ -559,7 +578,7 @@ header .actions { height: 3.8em; } -#bottom-bar div:first-child>* { +#bottom-bar>div:first-child>* { display: inline-block; vertical-align: middle; } @@ -568,6 +587,14 @@ header .actions { margin-right: .3em; } +#bottom-bar>*:first-child { + margin-right: auto; +} + +#more { + display: none; +} + #file-only { display: inline-block; border-right: 1px solid rgba(0, 0, 0, 0.075); @@ -584,55 +611,79 @@ header .actions { /* * * * * * * * * * * * * * * * - * BREADCRUMBS * + * DROPDOWN * * * * * * * * * * * * * * * * */ -#breadcrumbs-button { - padding: .4em 0.3em; - border-radius: .1em; - cursor: pointer; - transition: .1s ease all; -} - -#breadcrumbs-button.active, -#breadcrumbs-button:hover { - box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); - background: #fff; -} - -#current-file { - line-height: 2.7em; -} - -#breadcrumbs { - transition: .1s ease all; - padding: 0; - margin: 0; - list-style: none; - display: inline-flex; +.dropdown { + position: fixed; + top: -100%; + right: -100%; + visibility: hidden; + display: flex; flex-direction: column; - border-radius: 2px; + border-radius: .1em; border-top-left-radius: 0; box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); background: #fff; + z-index: 9999999; +} + +.dropdown.active { + right: .5em; + top: 4.5em; + visibility: visible; +} + +.dropdown .action { + padding: .7em; +} + +.dropdown i { + padding: 0; + vertical-align: middle; +} + +.dropdown span { + display: inline-block; + margin-left: .5em; + font-size: .9em; +} + + +/* * * * * * * * * * * * * * * * + * BREADCRUMBS * + * * * * * * * * * * * * * * * */ + +#previous { + margin-left: -.5em; +} + +#breadcrumbs { + list-style: none; + display: flex; + flex-direction: column; position: absolute; - left: 0; - top: 2.3em; + margin: 0; + padding: 0; min-width: 7em; - z-index: 999; - opacity: 0; - visibility: hidden; - color: #656565; } #breadcrumbs.active { opacity: 1; visibility: visible; + top: 0; + left: 0; + right: auto; } #breadcrumbs li { - line-height: 1.5em; - padding: .3em; + line-height: 1; + padding: .7em; + transition: .1s ease all; +} + +#breadcrumbs li:hover { + background-color: rgba(0, 0, 0, 0.04); } @@ -859,6 +910,39 @@ header .actions { } +/* * * * * * * * * * * * * * * * + * MULTIPLE SELECTION DIALOG * + * * * * * * * * * * * * * * * */ + +#multiple-selection { + position: fixed; + bottom: -4em; + left: 0; + z-index: 99999999; + width: 100%; + background-color: #2196f3; + height: 4em; + display: flex !important; + padding: 0.5em 0.5em 0.5em 1em; + justify-content: space-between; + align-items: center; + transition: .2s ease all; +} + +#multiple-selection.active { + bottom: 0; +} + +#multiple-selection * { + margin: 0; +} + +#multiple-selection p, +#multiple-selection i { + color: #fff; +} + + /* * * * * * * * * * * * * * * * * PROMPT * * * * * * * * * * * * * * * * */ @@ -992,6 +1076,12 @@ footer a:hover { * * * * * * * * * * * * * * * */ @media screen and (max-width: 650px) { + body { + transition: .2s ease padding; + } + .mobile-only { + display: inherit !important; + } #top-bar>div:nth-child(1) { display: none; } @@ -1008,6 +1098,44 @@ footer a:hover { width: calc(100% - 10em); padding: .5em; } + #main-actions { + position: fixed; + top: -100%; + right: -100%; + visibility: hidden; + display: flex; + flex-direction: column; + border-radius: .1em; + border-top-left-radius: 0; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); + background: #fff; + z-index: 9999999; + } + #main-actions.active { + right: .5em; + top: 4.5em; + visibility: visible; + } + #main-actions .action { + padding: .7em; + border-radius: 0; + } + #main-actions .action:hover { + background-color: rgba(0, 0, 0, 0.04); + } + #main-actions i { + padding: 0; + vertical-align: middle; + } + #main-actions .action:hover i { + padding: 0; + background-color: transparent; + } + #main-actions span { + display: inline-block; + margin-left: .5em; + font-size: .9em; + } } diff --git a/_embed/public/js/common.js b/_embed/public/js/common.js index 6d944a7d..89caaf00 100644 --- a/_embed/public/js/common.js +++ b/_embed/public/js/common.js @@ -3,7 +3,8 @@ var tempID = "_fm_internal_temporary_id", buttons = {}, templates = {}, - selectedItems = []; + selectedItems = [], + overlay, clickOverlay; // Removes an element, if exists, from an array Array.prototype.removeElement = function(element) { @@ -36,7 +37,8 @@ Element.prototype.changeToLoading = function() { element.style.opacity = 0; setTimeout(function() { - element.innerHTML = 'autorenew'; + element.classList.add('spin'); + element.innerHTML = 'autorenew'; element.style.opacity = 1; }, 200); @@ -60,13 +62,9 @@ Element.prototype.changeToDone = function(error, html) { } let firstStep = () => { - this.innerHTML = 'done'; - if (error) { - this.innerHTML = 'close'; - } - + this.classList.remove('spin'); + this.innerHTML = error ? 'close' : 'done'; this.style.opacity = 1; - setTimeout(secondStep, 1000); } @@ -109,7 +107,7 @@ var removeLastDirectoryPartOf = function(url) { * * * * * * * * * * * * * * * */ function closePrompt(event) { let prompt = document.querySelector('.prompt'); - + if (!prompt) return; event.preventDefault(); @@ -123,6 +121,7 @@ function closePrompt(event) { function notImplemented(event) { event.preventDefault(); + clickOverlay.click(); let clone = document.importNode(templates.info.content, true); clone.querySelector('h3').innerHTML = 'Not implemented'; @@ -172,7 +171,7 @@ function deleteSelected(single) { Array.from(selectedItems).forEach(id => { let request = new XMLHttpRequest(), - html = buttons.delete.changeToLoading(), + html = buttons.delete.querySelector('i').changeToLoading(), el, url; if (single) { @@ -194,7 +193,7 @@ function deleteSelected(single) { } } - buttons.delete.changeToDone(request.status != 204, html); + buttons.delete.querySelector('i').changeToDone(request.status != 204, html); } } @@ -216,13 +215,13 @@ function deleteEvent(event) { let clone = document.importNode(templates.question.content, true); clone.querySelector('h3').innerHTML = 'Delete files'; - + if (single) { - clone.querySelector('p').innerHTML = `Are you sure you want to delete this file/folder?`; + clone.querySelector('p').innerHTML = `Are you sure you want to delete this file/folder?`; } else { - clone.querySelector('p').innerHTML = `Are you sure you want to delete ${selectedItems.length} file(s)?`; + clone.querySelector('p').innerHTML = `Are you sure you want to delete ${selectedItems.length} file(s)?`; } - + clone.querySelector('input').remove(); clone.querySelector('.ok').innerHTML = 'Delete'; clone.querySelector('form').addEventListener('submit', deleteSelected(single)); @@ -360,29 +359,29 @@ function setupSearch() { function closeHelp(event) { event.preventDefault(); - + document.querySelector('.help').classList.remove('active'); document.querySelector('.overlay').classList.remove('active'); } function openHelp(event) { closePrompt(event); - + document.querySelector('.help').classList.add('active'); document.querySelector('.overlay').classList.add('active'); } window.addEventListener('keydown', (event) => { - if (event.keyCode == 27) { + if (event.keyCode == 27) { if (document.querySelector('.help.active')) { closeHelp(event); } } - + if (event.keyCode == 46) { deleteEvent(event); } - + if (event.keyCode == 112) { event.preventDefault(); openHelp(event); @@ -396,10 +395,13 @@ window.addEventListener('keydown', (event) => { * * * * * * * * * * * * * * * */ document.addEventListener("DOMContentLoaded", function(event) { + overlay = document.querySelector('.overlay'); + clickOverlay = document.querySelector('#click-overlay'); + buttons.logout = document.getElementById("logout"); buttons.open = document.getElementById("open"); buttons.delete = document.getElementById("delete"); - buttons.breadcrumbs = document.getElementById("breadcrumbs-button"); + buttons.previous = document.getElementById("previous"); // Attach event listeners buttons.logout.addEventListener("click", logoutEvent); @@ -412,21 +414,42 @@ document.addEventListener("DOMContentLoaded", function(event) { buttons.delete.addEventListener("click", deleteEvent); } - if (buttons.breadcrumbs) { - buttons.breadcrumbs.addEventListener("click", event => { - event.currentTarget.classList.toggle("active"); + if (buttons.previous) { + buttons.previous.addEventListener("click", event => { document.getElementById("breadcrumbs").classList.toggle("active"); + + clickOverlay.classList.add('active'); + + clickOverlay.addEventListener('click', event => { + document.getElementById("breadcrumbs").classList.remove("active"); + clickOverlay.classList.remove('active'); + }) }); } - document.querySelector('.overlay').addEventListener('click', event => { + overlay.addEventListener('click', event => { if (document.querySelector('.help.active')) { closeHelp(event); return; } - + closePrompt(event); }) + + let mainActions = document.getElementById('main-actions'); + + document.getElementById('more').addEventListener('click', event => { + event.preventDefault(); + event.stopPropagation(); + + clickOverlay.classList.add('active'); + mainActions.classList.add('active'); + + clickOverlay.addEventListener('click', event => { + mainActions.classList.remove('active'); + clickOverlay.classList.remove('active'); + }) + }) setupSearch(); return false; diff --git a/_embed/public/js/editor.js b/_embed/public/js/editor.js index 2c72fadb..c30ad255 100644 --- a/_embed/public/js/editor.js +++ b/_embed/public/js/editor.js @@ -209,7 +209,7 @@ document.addEventListener("DOMContentLoaded", (event) => { data.content = data.content.toString(); } - let html = button.changeToLoading(), + let html = button.querySelector('i').changeToLoading(), request = new XMLHttpRequest(); request.open("PUT", toWebDavURL(window.location.pathname)); @@ -217,7 +217,7 @@ document.addEventListener("DOMContentLoaded", (event) => { request.send(JSON.stringify(data)); request.onreadystatechange = function() { if (request.readyState == 4) { - button.changeToDone((request.status != 201), html); + button.querySelector('i').changeToDone((request.status != 201), html); } } } diff --git a/_embed/public/js/listing.js b/_embed/public/js/listing.js index 38bbdf57..a441ae91 100644 --- a/_embed/public/js/listing.js +++ b/_embed/public/js/listing.js @@ -1,6 +1,8 @@ 'use strict'; -var listing = {}; +var listing = { + selectMultiple: false +}; listing.reload = function(callback) { let request = new XMLHttpRequest(); @@ -126,7 +128,7 @@ listing.rename = function(event) { let newName = event.currentTarget.querySelector('input').value, newLink = removeLastDirectoryPartOf(toWebDavURL(link)) + "/" + newName, - html = buttons.rename.changeToLoading(), + html = buttons.rename.querySelector('i').changeToLoading(), request = new XMLHttpRequest(); request.open('MOVE', toWebDavURL(link)); @@ -147,7 +149,7 @@ listing.rename = function(event) { }); } - buttons.rename.changeToDone((request.status != 201 && request.status != 204), html); + buttons.rename.querySelector('i').changeToDone((request.status != 201 && request.status != 204), html); } } @@ -169,7 +171,7 @@ listing.rename = function(event) { listing.handleFiles = function(files, base) { let button = document.getElementById("upload"), - html = button.changeToLoading(); + html = button.querySelector('i').changeToLoading(); for (let i = 0; i < files.length; i++) { let request = new XMLHttpRequest(); @@ -182,7 +184,7 @@ listing.handleFiles = function(files, base) { listing.reload(); } - button.changeToDone((request.status != 201), html); + button.querySelector('i').changeToDone((request.status != 201), html); } } } @@ -222,7 +224,7 @@ listing.handleSelectionChange = function(event) { } else { buttons.open.classList.remove("disabled"); } - + buttons.rename.classList.remove("disabled"); } @@ -259,7 +261,7 @@ listing.selectItem = function(event) { if (selectedItems.length != 0) event.preventDefault(); if (selectedItems.indexOf(el.id) == -1) { - if (!event.ctrlKey) listing.unselectAll(); + if (!event.ctrlKey && !listing.selectMultiple) listing.unselectAll(); el.setAttribute("aria-selected", true); selectedItems.push(el.id); @@ -290,7 +292,7 @@ listing.newFilePrompt = function(event) { event.preventDefault(); let button = document.getElementById('new'), - html = button.changeToLoading(), + html = button.querySelector('i').changeToLoading(), request = new XMLHttpRequest(), name = event.currentTarget.querySelector('input').value; @@ -298,7 +300,7 @@ listing.newFilePrompt = function(event) { request.send(); request.onreadystatechange = function() { if (request.readyState == 4) { - button.changeToDone((request.status != 201), html); + button.querySelector('i').changeToDone((request.status != 201), html); listing.reload(); } } @@ -322,13 +324,12 @@ window.addEventListener('keydown', (event) => { if (document.querySelectorAll('.prompt').length) { closePrompt(event); } - } if (event.keyCode == 113) { listing.rename(); } - + if (event.ctrlKey || event.metaKey) { console.log("hey") switch (String.fromCharCode(event.which).toLowerCase()) { @@ -351,9 +352,26 @@ document.addEventListener('DOMContentLoaded', event => { buttons.new = document.getElementById('new'); buttons.download = document.getElementById('download'); + document.getElementById('multiple-selection-activate').addEventListener('click', event => { + listing.selectMultiple = true; + clickOverlay.click(); + + document.getElementById('multiple-selection').classList.add('active'); + document.querySelector('body').style.paddingBottom = "4em"; + }) + + document.getElementById('multiple-selection-cancel').addEventListener('click', event => { + listing.selectMultiple = false; + + document.querySelector('body').style.paddingBottom = "0"; + document.getElementById('multiple-selection').classList.remove('active'); + }) + if (user.AllowEdit) { buttons.rename.addEventListener("click", listing.rename); } + + let items = document.getElementsByClassName('item'); if (user.AllowNew) { buttons.upload.addEventListener("click", (event) => { @@ -363,7 +381,6 @@ document.addEventListener('DOMContentLoaded', event => { buttons.new.addEventListener('click', listing.newFileButton); // Drag and Drop - let items = document.getElementsByClassName('item'); document.addEventListener("dragover", function(event) { event.preventDefault(); }, false); @@ -382,4 +399,26 @@ document.addEventListener('DOMContentLoaded', event => { document.addEventListener("drop", listing.documentDrop, false); } + + let touches = { + id: '', + count: 0 + }; + + Array.from(items).forEach(file => { + file.addEventListener('touchstart', event => { + if (touches.id != file.id) { + touches.id = file.id; + touches.count = 1; + + return; + } + + touches.count++; + + if (touches.count > 1) { + window.location = file.dataset.url; + } + }); + }); }); \ No newline at end of file diff --git a/_embed/templates/base.tmpl b/_embed/templates/base.tmpl index 0d829bfc..fe7ebcf7 100644 --- a/_embed/templates/base.tmpl +++ b/_embed/templates/base.tmpl @@ -3,7 +3,7 @@ {{ $absURL := .Config.AbsoluteURL }} {{.Name}} - + @@ -47,69 +47,81 @@
- {{- if ne .Name "/"}} - - {{- end }} -

{{ if ne .Name "/"}}{{ .Name }}{{ else }}Root{{ end }}

+ +

{{ if ne .Name "/"}}{{ .Name }}{{ else }}Root{{ end }}

-
-
- {{- if and (not .IsDir) (.User.AllowEdit) }} - {{- if .Editor}} - {{- if eq .Data.Mode "markdown" }} -
- remove_red_eye -
- {{- end }} - {{- end }} - -
- save -
- {{- end }} - -
- open_in_new -
- - {{- if and .IsDir .User.AllowEdit }} -
- mode_edit -
- {{- end }} +
+ more_vert +
+ +
+ {{- if and (not .IsDir) (.User.AllowEdit) }} + {{- if .Editor}} + {{- if eq .Data.Mode "markdown" }} +
+ remove_red_eye +
+ {{- end }} + {{- end }} - {{- if .User.AllowEdit }} -
- delete Delete -
- {{- end }} +
+ save +
+ {{- end }} + +
+ open_in_new
+ {{- if and .IsDir .User.AllowEdit }} +
+ mode_edit +
+ {{- end }} + + {{- if .User.AllowEdit }} +
+ deleteDelete +
+ {{- end }} +
+ +
{{- if .IsDir }}
{{- if eq .Display "mosaic" }} - view_list + view_listSwitch view {{- else }} - view_module + view_moduleSwitch view {{- end }}
+ +
+ check_circleSelect +
{{- end }} {{- if and (.User.AllowNew) (.IsDir) }}
- file_upload Upload + file_uploadUpload
{{- end }}
- file_download Download + file_downloadDownload {{- if .IsDir }}
- info + infoInfo
+ +
- + +
+

Multiple selection enabled

+
+ clear +
+
+
{{- template "content" . }}
diff --git a/_embed/templates/listing.tmpl b/_embed/templates/listing.tmpl index 4a512da3..f23e9b07 100644 --- a/_embed/templates/listing.tmpl +++ b/_embed/templates/listing.tmpl @@ -36,7 +36,7 @@

Folders

{{- range .Items }} - {{- if and (.UserAllowed) (.IsDir) }} + {{- if (.IsDir) }} {{ template "item" .}} {{- end }} {{- end }} @@ -47,7 +47,7 @@

Files

{{- range .Items }} - {{- if and (.UserAllowed) (not .IsDir) }} + {{- if (not .IsDir) }} {{ template "item" .}} {{- end }} {{- end }} @@ -55,7 +55,7 @@ {{- end }}
- + {{- end -}} {{- end -}} diff --git a/file/listing.go b/file/listing.go index 575c5227..a14cff99 100644 --- a/file/listing.go +++ b/file/listing.go @@ -57,6 +57,11 @@ func GetListing(u *config.User, filePath string, baseURL string) (*Listing, erro for _, f := range files { name := f.Name() + allowed := u.Allowed("/" + name) + + if !allowed { + continue + } if f.IsDir() { name += "/" @@ -71,7 +76,7 @@ func GetListing(u *config.User, filePath string, baseURL string) (*Listing, erro i := Info{ FileInfo: f, URL: url.String(), - UserAllowed: u.Allowed("/" + name), + UserAllowed: allowed, } i.RetrieveFileType() diff --git a/page/error.go b/page/error.go index fea2debf..cdd470f7 100644 --- a/page/error.go +++ b/page/error.go @@ -10,6 +10,8 @@ const errTemplate = ` TITLE + +