Unify tables and listings
This commit is contained in:
+1
-1
@@ -115,7 +115,7 @@
|
||||
function wireFileLinks() {
|
||||
if (!state.available) return;
|
||||
document.addEventListener('click', function (e) {
|
||||
var item = e.target.closest && e.target.closest('.listing-item');
|
||||
var item = e.target.closest && e.target.closest('.list-item');
|
||||
if (!item) return;
|
||||
var anchor = e.target.closest('a');
|
||||
if (!anchor) return;
|
||||
|
||||
@@ -9,15 +9,17 @@
|
||||
{{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>
|
||||
<div class="listing panel">
|
||||
<table class="data-table panel">
|
||||
<tbody>
|
||||
{{range .Entries}}
|
||||
<div class="listing-item" data-path="{{.URL}}">
|
||||
<span class="icon">{{.Icon}}</span>
|
||||
<a href="{{.URL}}">{{.Name}}</a>
|
||||
<span class="meta">{{.Meta}}</span>
|
||||
</div>
|
||||
<tr class="list-item" data-path="{{.URL}}">
|
||||
<td class="icon">{{.Icon}}</td>
|
||||
<td class="name"><a href="{{.URL}}">{{.Name}}</a></td>
|
||||
<td class="meta">{{.Meta}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
{{else if not .Content}}
|
||||
{{if not .SpecialContent}}
|
||||
<p class="empty">Empty folder — <a href="?edit">[CREATE]</a></p>
|
||||
|
||||
+31
-21
@@ -70,6 +70,15 @@
|
||||
dropdown.className = 'suggest-dropdown';
|
||||
host.appendChild(dropdown);
|
||||
|
||||
function makeRow(cls, tabbable) {
|
||||
var tr = document.createElement('tr');
|
||||
tr.className = cls;
|
||||
if (tabbable) tr.setAttribute('tabindex', '0');
|
||||
var td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
return { tr: tr, td: td };
|
||||
}
|
||||
|
||||
var state = {
|
||||
results: [],
|
||||
total: 0,
|
||||
@@ -102,49 +111,50 @@
|
||||
dropdown.classList.remove('is-open');
|
||||
return;
|
||||
}
|
||||
var table = document.createElement('table');
|
||||
table.className = 'data-table';
|
||||
var tbody = document.createElement('tbody');
|
||||
table.appendChild(tbody);
|
||||
|
||||
var tokens = tokenize(state.query);
|
||||
if (state.results.length === 0) {
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'suggest-row is-empty';
|
||||
empty.textContent = 'No matches';
|
||||
dropdown.appendChild(empty);
|
||||
var empty = makeRow('is-empty', false);
|
||||
empty.td.textContent = 'No matches';
|
||||
tbody.appendChild(empty.tr);
|
||||
} else {
|
||||
state.results.forEach(function (r, i) {
|
||||
var row = document.createElement('button');
|
||||
row.type = 'button';
|
||||
row.className = 'suggest-row';
|
||||
row.setAttribute('data-idx', String(i));
|
||||
var row = makeRow('suggest-row', true);
|
||||
row.tr.setAttribute('data-idx', String(i));
|
||||
var nameEl = document.createElement('span');
|
||||
nameEl.className = 'suggest-name';
|
||||
nameEl.innerHTML = highlight(r.name, tokens);
|
||||
var pathEl = document.createElement('span');
|
||||
pathEl.className = 'suggest-path';
|
||||
pathEl.textContent = '/' + r.path;
|
||||
row.appendChild(nameEl);
|
||||
row.appendChild(pathEl);
|
||||
if (i === state.activeIdx) row.classList.add('is-active');
|
||||
row.addEventListener('mousedown', function (e) {
|
||||
row.td.appendChild(nameEl);
|
||||
row.td.appendChild(pathEl);
|
||||
if (i === state.activeIdx) row.tr.classList.add('is-active');
|
||||
row.tr.addEventListener('mousedown', function (e) {
|
||||
// mousedown (not click) so the input doesn't blur-close
|
||||
// the dropdown before the pick handler fires.
|
||||
e.preventDefault();
|
||||
pick(i);
|
||||
});
|
||||
dropdown.appendChild(row);
|
||||
tbody.appendChild(row.tr);
|
||||
});
|
||||
if (opts.showFooter && state.total > state.results.length) {
|
||||
var footer = document.createElement('button');
|
||||
footer.type = 'button';
|
||||
footer.className = 'suggest-row suggest-footer';
|
||||
footer.textContent = 'Show all ' + state.total + ' matches';
|
||||
var footer = makeRow('suggest-row suggest-footer', true);
|
||||
footer.td.textContent = 'Show all ' + state.total + ' matches';
|
||||
var footerIdx = state.results.length;
|
||||
if (state.activeIdx === footerIdx) footer.classList.add('is-active');
|
||||
footer.addEventListener('mousedown', function (e) {
|
||||
if (state.activeIdx === footerIdx) footer.tr.classList.add('is-active');
|
||||
footer.tr.addEventListener('mousedown', function (e) {
|
||||
e.preventDefault();
|
||||
pickFooter();
|
||||
});
|
||||
dropdown.appendChild(footer);
|
||||
tbody.appendChild(footer.tr);
|
||||
}
|
||||
}
|
||||
dropdown.appendChild(table);
|
||||
dropdown.classList.add('is-open');
|
||||
}
|
||||
|
||||
@@ -225,7 +235,7 @@
|
||||
state.activeIdx = next;
|
||||
render();
|
||||
// Keep the active row in view.
|
||||
var active = dropdown.querySelector('.suggest-row.is-active');
|
||||
var active = dropdown.querySelector('tr.is-active');
|
||||
if (active && active.scrollIntoView) {
|
||||
try { active.scrollIntoView({ block: 'nearest' }); } catch (e) {}
|
||||
}
|
||||
|
||||
+45
-46
@@ -257,18 +257,6 @@ main > h2 {
|
||||
margin: var(--space-3) 0;
|
||||
}
|
||||
.content pre code { background: none; padding: 0; }
|
||||
.content table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: var(--space-3) 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.content th, .content td {
|
||||
border: var(--border);
|
||||
padding: 0.4rem var(--space-3);
|
||||
text-align: left;
|
||||
}
|
||||
.content th { background: var(--bg-panel); color: var(--text); }
|
||||
.content hr { margin: var(--space-5) 0; }
|
||||
.content img { max-width: 100%; }
|
||||
.content li:has(> input.task-checkbox:checked) {
|
||||
@@ -288,22 +276,38 @@ main > h2 {
|
||||
}
|
||||
.heading-anchor .dropdown-toggle:hover { color: var(--primary-hover); }
|
||||
|
||||
/* === Listing rows === */
|
||||
.listing-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: 0.6rem var(--space-4);
|
||||
font-size: 0.95rem;
|
||||
/* === Data tables ===
|
||||
Shared style for the file listing, search-suggestion dropdown, and
|
||||
markdown content tables. .data-table-grid adds per-cell borders + a
|
||||
header band for content (markdown) tables. */
|
||||
.data-table { width: 100%; border-collapse: collapse; }
|
||||
.data-table th,
|
||||
.data-table td { padding: 0.4rem var(--space-3); text-align: left; }
|
||||
.data-table:not(.data-table-grid) tbody tr + tr { border-top: var(--border); }
|
||||
.data-table:not(.data-table-grid) tbody tr:hover,
|
||||
.data-table tr.is-active { background: var(--bg-panel-hover); }
|
||||
.data-table tbody tr.is-empty,
|
||||
.data-table tbody tr.is-empty:hover {
|
||||
color: var(--text-muted);
|
||||
background: none;
|
||||
cursor: default;
|
||||
}
|
||||
.listing-item + .listing-item { border-top: var(--border); }
|
||||
.listing-item:hover { background: var(--bg-panel-hover); }
|
||||
.listing-item .icon { width: 1.25rem; text-align: center; flex-shrink: 0; }
|
||||
.listing-item a { flex: 1; overflow-wrap: anywhere; color: inherit; }
|
||||
.listing-item .meta {
|
||||
.data-table-grid th,
|
||||
.data-table-grid td { border: var(--border); }
|
||||
.data-table-grid th { background: var(--bg-panel); color: var(--text); }
|
||||
.content .data-table-grid { margin: var(--space-3) 0; font-size: 0.9rem; }
|
||||
|
||||
/* File listing rows */
|
||||
.list-item { font-size: 0.95rem; }
|
||||
.list-item > td { padding: 0.6rem var(--space-4); }
|
||||
.list-item td.icon { width: 1.25rem; text-align: center; }
|
||||
.list-item td.name { overflow-wrap: anywhere; }
|
||||
.list-item td.name a { color: inherit; display: block; }
|
||||
.list-item td.meta {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* === Dropdown menu === */
|
||||
@@ -339,27 +343,12 @@ main > h2 {
|
||||
display: none;
|
||||
}
|
||||
.suggest-dropdown.is-open { display: block; }
|
||||
.suggest-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.1rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: none;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
.suggest-row + .suggest-row { border-top: var(--border); }
|
||||
.suggest-row:hover,
|
||||
.suggest-row.is-active { background: var(--bg-panel-hover); }
|
||||
.suggest-row.is-empty { color: var(--text-muted); cursor: default; }
|
||||
.suggest-row.is-empty:hover { background: none; }
|
||||
.suggest-row { cursor: pointer; }
|
||||
.suggest-row > td { padding: 0.4rem 0.6rem; }
|
||||
.suggest-name, .suggest-path { display: block; }
|
||||
.suggest-name { color: var(--text); }
|
||||
.suggest-path { color: var(--text-muted); font-size: 0.8rem; }
|
||||
.suggest-footer { color: var(--link); font-size: var(--font-sm); }
|
||||
.suggest-path { color: var(--text-muted); font-size: 0.8rem; margin-top: 0.1rem; }
|
||||
.suggest-footer > td { color: var(--link); font-size: var(--font-sm); }
|
||||
|
||||
/* === Editor toolbar === */
|
||||
.editor-toolbar {
|
||||
@@ -650,6 +639,16 @@ aside.sidebar:empty { display: none; }
|
||||
.modal-backdrop { padding: var(--space-2); align-items: flex-start; }
|
||||
.modal { max-width: none; margin-top: var(--space-4); }
|
||||
.modal .panel-header { cursor: default; }
|
||||
.listing-item { flex-wrap: wrap; }
|
||||
.listing-item .meta { flex-basis: 100%; padding-left: calc(1.25rem + var(--space-3)); }
|
||||
/* On mobile, switch .list-item from a table row to a CSS grid so the
|
||||
meta cell wraps to its own line indented under the name. */
|
||||
.list-item {
|
||||
display: grid;
|
||||
grid-template-columns: 1.25rem 1fr;
|
||||
gap: 0 var(--space-3);
|
||||
padding: 0.6rem var(--space-4);
|
||||
}
|
||||
.list-item > td { padding: 0; }
|
||||
.list-item td.icon { grid-row: 1; grid-column: 1; }
|
||||
.list-item td.name { grid-row: 1; grid-column: 2; }
|
||||
.list-item td.meta { grid-row: 2; grid-column: 2; text-align: left; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user