View switching feature
This commit is contained in:
+13
-1
@@ -8,7 +8,17 @@
|
||||
<div class="content">{{.SpecialContent}}</div>
|
||||
{{end}}
|
||||
{{if .Entries}}
|
||||
<h2 id="files">Files <button class="btn btn-small" data-companion-reveal hidden title="Open folder in file manager">open</button></h2>
|
||||
<h2 id="files">Files <button class="btn btn-small" data-companion-reveal hidden title="Open folder in file manager">open</button>{{if .CanEdit}} <button class="btn btn-small" id="view-settings-btn" onclick="openViewSettings()" title="View & sorting" data-view="{{.View}}" data-sort="{{.Sort}}" data-order="{{.Order}}">view</button>{{end}}</h2>
|
||||
{{if eq .View "thumbnail"}}
|
||||
<div class="thumb-grid">
|
||||
{{range .Entries}}
|
||||
<a class="thumb-tile" href="{{.URL}}" title="{{.Name}}">
|
||||
{{if .ThumbURL}}<img class="thumb-img" src="{{.ThumbURL}}" alt="" loading="lazy" width="300">{{else}}<span class="thumb-icon">{{.Icon}}</span>{{end}}
|
||||
<span class="thumb-label truncate">{{.Name}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<table class="data-table panel">
|
||||
<tbody>
|
||||
{{range .Entries}}
|
||||
@@ -20,6 +30,8 @@
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
{{if .CanEdit}}<script src="/_/page/view-settings.js"></script>{{end}}
|
||||
{{else if not .Content}}
|
||||
{{if not .SpecialContent}}
|
||||
<p class="empty">Empty folder — <a href="?edit">[CREATE]</a></p>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// View-settings modal: lets the user pick the folder listing's view style,
|
||||
// sort key, and order, then persists them by POSTing to the folder with
|
||||
// ?settings. Reuses openModal/closeModal and postReplace from page/actions.js.
|
||||
function openViewSettings() {
|
||||
var btn = document.getElementById('view-settings-btn');
|
||||
var state = {
|
||||
view: (btn && btn.dataset.view) || 'list',
|
||||
sort: (btn && btn.dataset.sort) || 'name',
|
||||
order: (btn && btn.dataset.order) || 'asc'
|
||||
};
|
||||
|
||||
// segmented builds a row of mutually-exclusive .btn toggles bound to a
|
||||
// single state key, marking the current choice with .is-active.
|
||||
function segmented(key, options) {
|
||||
var wrap = document.createElement('div');
|
||||
wrap.className = 'row gap-1';
|
||||
options.forEach(function (opt) {
|
||||
var b = document.createElement('button');
|
||||
b.type = 'button';
|
||||
b.className = 'btn btn-small';
|
||||
b.textContent = opt.label;
|
||||
if (state[key] === opt.value) b.classList.add('is-active');
|
||||
b.addEventListener('click', function () {
|
||||
state[key] = opt.value;
|
||||
wrap.querySelectorAll('button').forEach(function (x) {
|
||||
x.classList.remove('is-active');
|
||||
});
|
||||
b.classList.add('is-active');
|
||||
});
|
||||
wrap.appendChild(b);
|
||||
});
|
||||
return wrap;
|
||||
}
|
||||
|
||||
function field(labelText, control) {
|
||||
var row = document.createElement('div');
|
||||
row.className = 'col gap-1';
|
||||
var label = document.createElement('span');
|
||||
label.className = 'caption';
|
||||
label.textContent = labelText;
|
||||
row.appendChild(label);
|
||||
row.appendChild(control);
|
||||
return row;
|
||||
}
|
||||
|
||||
var sortSelect = document.createElement('select');
|
||||
sortSelect.className = 'input';
|
||||
[['name', 'Name'], ['modified', 'Modified'], ['size', 'Size']].forEach(function (o) {
|
||||
var opt = document.createElement('option');
|
||||
opt.value = o[0];
|
||||
opt.textContent = o[1];
|
||||
if (state.sort === o[0]) opt.selected = true;
|
||||
sortSelect.appendChild(opt);
|
||||
});
|
||||
sortSelect.addEventListener('change', function () { state.sort = sortSelect.value; });
|
||||
|
||||
var body = document.createElement('div');
|
||||
body.className = 'col';
|
||||
body.appendChild(field('View style', segmented('view', [
|
||||
{ value: 'list', label: 'List' },
|
||||
{ value: 'thumbnail', label: 'Thumbnail' }
|
||||
])));
|
||||
body.appendChild(field('Sort by', sortSelect));
|
||||
body.appendChild(field('Order', segmented('order', [
|
||||
{ value: 'asc', label: 'Asc' },
|
||||
{ value: 'desc', label: 'Desc' }
|
||||
])));
|
||||
|
||||
openModal({
|
||||
title: 'View settings',
|
||||
body: body,
|
||||
confirm: {
|
||||
label: 'SAVE',
|
||||
onConfirm: function () {
|
||||
var action = window.location.pathname + '?settings';
|
||||
var formBody = 'view=' + encodeURIComponent(state.view) +
|
||||
'&sort=' + encodeURIComponent(state.sort) +
|
||||
'&order=' + encodeURIComponent(state.order);
|
||||
var target = window.location.pathname;
|
||||
closeModal();
|
||||
postReplace(action, formBody, target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -192,6 +192,8 @@ footer {
|
||||
.btn-fab:hover { background: var(--bg-panel-hover); color: var(--primary-hover); }
|
||||
.danger { color: var(--danger); }
|
||||
.danger:hover { color: var(--danger-hover); }
|
||||
/* Selected segmented-toggle button (view-settings modal). */
|
||||
.btn.is-active { color: var(--primary-hover); }
|
||||
|
||||
/* === Form controls ===
|
||||
.input baseline is shared by search-input, modal inputs, and the editor
|
||||
@@ -435,6 +437,43 @@ button.fab { display: none; }
|
||||
background: var(--bg-panel) url("/_/icons/thumb-placeholder.svg") center/2rem no-repeat;
|
||||
}
|
||||
|
||||
/* === Thumbnail listing grid ===
|
||||
File-listing variant of .photo-grid: responsive tiles that pair a thumbnail
|
||||
(or a file/folder icon for non-thumbnailable entries) with a truncated
|
||||
name label beneath. */
|
||||
.thumb-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: var(--space-3);
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
.thumb-tile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
color: var(--text);
|
||||
border: var(--border);
|
||||
background: var(--bg-panel);
|
||||
padding: var(--space-2);
|
||||
}
|
||||
.thumb-tile:hover { background: var(--bg-panel-hover); color: var(--primary-hover); }
|
||||
.thumb-img {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
background: var(--bg) url("/_/icons/thumb-placeholder.svg") center/2rem no-repeat;
|
||||
}
|
||||
.thumb-icon {
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3rem;
|
||||
color: var(--secondary);
|
||||
}
|
||||
.thumb-label { font-size: var(--font-sm); }
|
||||
|
||||
.empty { padding: var(--space-4); text-align: center; }
|
||||
|
||||
/* === Scrollbars === */
|
||||
|
||||
Reference in New Issue
Block a user