Compare commits

..

2 Commits

Author SHA1 Message Date
luxick 20a6bac3d6 Unify tables and listings 2026-05-27 20:09:52 +02:00
luxick e089e0b2c3 CSS Refactor 2026-05-27 10:52:47 +02:00
15 changed files with 434 additions and 805 deletions
+1 -1
View File
@@ -115,7 +115,7 @@
function wireFileLinks() { function wireFileLinks() {
if (!state.available) return; if (!state.available) return;
document.addEventListener('click', function (e) { 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; if (!item) return;
var anchor = e.target.closest('a'); var anchor = e.target.closest('a');
if (!anchor) return; if (!anchor) return;
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="diary-cal"> <div class="diary-cal panel panel-sidebar">
<div class="panel-header"><a href="{{.DiaryURL}}">Chronological</a></div> <div class="panel-header"><a href="{{.DiaryURL}}">Chronological</a></div>
<div class="diary-cal-nav"> <div class="diary-cal-nav">
<a href="{{.MonthURL}}" class="diary-cal-heading">{{.MonthName}}</a> <a href="{{.MonthURL}}" class="diary-cal-heading">{{.MonthName}}</a>
+1 -1
View File
@@ -70,7 +70,7 @@
<span class="toolbar-sep"></span> <span class="toolbar-sep"></span>
<button type="button" class="btn btn-tool" data-action="wide" data-key="Z" title="Toggle wide mode (Z)"></button> <button type="button" class="btn btn-tool" data-action="wide" data-key="Z" title="Toggle wide mode (Z)"></button>
</div> </div>
<textarea name="content" id="editor" autofocus>{{.RawContent}}</textarea> <textarea class="input editor-textarea" name="content" id="editor" autofocus>{{.RawContent}}</textarea>
</form> </form>
<script src="/_/editor/lists.js"></script> <script src="/_/editor/lists.js"></script>
<script src="/_/editor/tables.js"></script> <script src="/_/editor/tables.js"></script>
+2 -2
View File
@@ -85,13 +85,13 @@
var targetWrap = document.createElement('div'); var targetWrap = document.createElement('div');
var targetInput = document.createElement('input'); var targetInput = document.createElement('input');
targetInput.type = 'text'; targetInput.type = 'text';
targetInput.className = 'modal-input'; targetInput.className = 'input';
targetInput.placeholder = 'Page path or search…'; targetInput.placeholder = 'Page path or search…';
targetWrap.appendChild(targetInput); targetWrap.appendChild(targetInput);
var displayInput = document.createElement('input'); var displayInput = document.createElement('input');
displayInput.type = 'text'; displayInput.type = 'text';
displayInput.className = 'modal-input'; displayInput.className = 'input';
displayInput.placeholder = 'Display text (optional)'; displayInput.placeholder = 'Display text (optional)';
if (sel) displayInput.value = sel; if (sel) displayInput.value = sel;
+2 -2
View File
@@ -105,7 +105,7 @@ window.EditorMovie = (function () {
var input = document.createElement('input'); var input = document.createElement('input');
input.type = 'text'; input.type = 'text';
input.className = 'modal-input'; input.className = 'input';
input.placeholder = 'OMDb API key'; input.placeholder = 'OMDb API key';
body.appendChild(input); body.appendChild(input);
@@ -128,7 +128,7 @@ window.EditorMovie = (function () {
function importWithKey(textarea, key, initialTitle) { function importWithKey(textarea, key, initialTitle) {
var input = document.createElement('input'); var input = document.createElement('input');
input.type = 'text'; input.type = 'text';
input.className = 'modal-input'; input.className = 'input';
input.placeholder = 'Title, optionally with (YYYY)'; input.placeholder = 'Title, optionally with (YYYY)';
input.value = initialTitle; input.value = initialTitle;
+3 -3
View File
@@ -17,16 +17,16 @@
</head> </head>
<body> <body>
<header> <header>
<nav class="breadcrumb"> <nav class="breadcrumb row">
<a href="/" tabindex="-1" title="Home"><svg class="logo" viewBox="0 0 26.052269 26.052269" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linejoin="miter" transform="matrix(0.05463483,8.1519706e-6,-8.1519706e-6,0.05463483,-64.560546,-24.6949)"><rect x="1188.537" y="457.92056" width="461.87488" height="462.15189" stroke-width="20.2288"/><path d="m1348.9955 456.59572.046 309.36839" stroke-width="19.6849"/><path d="m1200.3996 765.80237 441.8362-.0659" stroke-width="19.6849"/><path d="m1648.2897 620.244-299.2012.0446" stroke-width="20.5676"/><path d="m1491.6148 909.24806-.021-136.93117" stroke-width="19.6849"/><rect x="1191.6504" y="461.66092" width="457.09634" height="457.09634" stroke-width="19.6761"/></g></svg></a> <a href="/" tabindex="-1" title="Home"><svg class="logo" viewBox="0 0 26.052269 26.052269" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="currentColor" stroke-linejoin="miter" transform="matrix(0.05463483,8.1519706e-6,-8.1519706e-6,0.05463483,-64.560546,-24.6949)"><rect x="1188.537" y="457.92056" width="461.87488" height="462.15189" stroke-width="20.2288"/><path d="m1348.9955 456.59572.046 309.36839" stroke-width="19.6849"/><path d="m1200.3996 765.80237 441.8362-.0659" stroke-width="19.6849"/><path d="m1648.2897 620.244-299.2012.0446" stroke-width="20.5676"/><path d="m1491.6148 909.24806-.021-136.93117" stroke-width="19.6849"/><rect x="1191.6504" y="461.66092" width="457.09634" height="457.09634" stroke-width="19.6761"/></g></svg></a>
{{if .ParentURL}}<a class="nav-up" href="{{.ParentURL}}" tabindex="-1" title="Up" aria-label="Up">Up <svg viewBox="0 0 16 16" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="miter" stroke-linecap="square"><path d="M8 13V3M3 8l5-5 5 5"/></svg></a>{{end}} {{if .ParentURL}}<a class="nav-up" href="{{.ParentURL}}" tabindex="-1" title="Up" aria-label="Up">Up <svg viewBox="0 0 16 16" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="miter" stroke-linecap="square"><path d="M8 13V3M3 8l5-5 5 5"/></svg></a>{{end}}
</nav> </nav>
{{if not .EditMode}} {{if not .EditMode}}
<form class="search-form" action="/" method="get"> <form class="search-form" action="/" method="get">
<input class="search-input" type="search" name="q" value="{{block "searchQuery" .}}{{end}}" placeholder="Search…" title="Search (F)" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /> <input class="input search-input" type="search" name="q" value="{{block "searchQuery" .}}{{end}}" placeholder="Search…" title="Search (F)" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
</form> </form>
{{end}} {{end}}
<div class="header-actions">{{block "headerActions" .}}{{end}}</div> <div class="header-actions row">{{block "headerActions" .}}{{end}}</div>
</header> </header>
<div class="page-wrap"> <div class="page-wrap">
<main> <main>
+4 -4
View File
@@ -15,20 +15,20 @@
backdrop.className = 'modal-backdrop'; backdrop.className = 'modal-backdrop';
modal = document.createElement('div'); modal = document.createElement('div');
modal.className = 'modal'; modal.className = 'modal panel panel-floating';
modal.setAttribute('role', 'dialog'); modal.setAttribute('role', 'dialog');
modal.setAttribute('aria-modal', 'true'); modal.setAttribute('aria-modal', 'true');
var header = document.createElement('div'); var header = document.createElement('div');
header.className = 'modal-header'; header.className = 'panel-header';
titleEl = document.createElement('span'); titleEl = document.createElement('span');
header.appendChild(titleEl); header.appendChild(titleEl);
bodyEl = document.createElement('div'); bodyEl = document.createElement('div');
bodyEl.className = 'modal-body'; bodyEl.className = 'panel-body';
footerEl = document.createElement('div'); footerEl = document.createElement('div');
footerEl.className = 'modal-footer'; footerEl.className = 'panel-footer';
cancelBtn = document.createElement('button'); cancelBtn = document.createElement('button');
cancelBtn.type = 'button'; cancelBtn.type = 'button';
+3 -3
View File
@@ -36,7 +36,7 @@ function postReplace(action, body, target) {
function promptPageName(title, initial, confirmLabel, onName) { function promptPageName(title, initial, confirmLabel, onName) {
var input = document.createElement('input'); var input = document.createElement('input');
input.type = 'text'; input.type = 'text';
input.className = 'modal-input'; input.className = 'input';
input.placeholder = 'Page name'; input.placeholder = 'Page name';
if (initial) input.value = initial; if (initial) input.value = initial;
openModal({ openModal({
@@ -89,7 +89,7 @@ function movePage() {
onSelect: function (newParent) { onSelect: function (newParent) {
var input = document.createElement('input'); var input = document.createElement('input');
input.type = 'text'; input.type = 'text';
input.className = 'modal-input'; input.className = 'input';
input.placeholder = 'Page name'; input.placeholder = 'Page name';
input.value = currentName; input.value = currentName;
@@ -99,7 +99,7 @@ function movePage() {
var linksLabel = document.createElement('label'); var linksLabel = document.createElement('label');
linksLabel.htmlFor = linksCheckbox.id; linksLabel.htmlFor = linksCheckbox.id;
linksLabel.className = 'modal-checkbox'; linksLabel.className = 'row';
linksLabel.appendChild(linksCheckbox); linksLabel.appendChild(linksCheckbox);
linksLabel.appendChild(document.createTextNode('Update links')); linksLabel.appendChild(document.createTextNode('Update links'));
+1 -1
View File
@@ -42,7 +42,7 @@
function addTask(sectionIndex, headingId) { function addTask(sectionIndex, headingId) {
var input = document.createElement('input'); var input = document.createElement('input');
input.type = 'text'; input.type = 'text';
input.className = 'modal-input'; input.className = 'input';
input.placeholder = 'Task description'; input.placeholder = 'Task description';
var ctrl = openModal({ var ctrl = openModal({
title: 'Add task', title: 'Add task',
+10 -8
View File
@@ -9,15 +9,17 @@
{{end}} {{end}}
{{if .Entries}} {{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></h2>
<div class="listing"> <table class="data-table panel">
<tbody>
{{range .Entries}} {{range .Entries}}
<div class="listing-item" data-path="{{.URL}}"> <tr class="list-item" data-path="{{.URL}}">
<span class="icon">{{.Icon}}</span> <td class="icon">{{.Icon}}</td>
<a href="{{.URL}}">{{.Name}}</a> <td class="name"><a href="{{.URL}}">{{.Name}}</a></td>
<span class="meta">{{.Meta}}</span> <td class="meta">{{.Meta}}</td>
</div> </tr>
{{end}} {{end}}
</div> </tbody>
</table>
{{else if not .Content}} {{else if not .Content}}
{{if not .SpecialContent}} {{if not .SpecialContent}}
<p class="empty">Empty folder — <a href="?edit">[CREATE]</a></p> <p class="empty">Empty folder — <a href="?edit">[CREATE]</a></p>
@@ -35,7 +37,7 @@
<script src="/_/page/sidebar-fab.js"></script> <script src="/_/page/sidebar-fab.js"></script>
{{end}} {{end}}
{{define "sidebar"}}{{if .CanEdit}}<nav class="actions"> {{define "sidebar"}}{{if .CanEdit}}<nav class="actions panel panel-sidebar">
<div class="panel-header">ACTIONS</div> <div class="panel-header">ACTIONS</div>
<button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW PAGE</button> <button class="btn btn-block" onclick="newPage()" title="New page (N)">NEW PAGE</button>
<a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT PAGE</a> <a class="btn btn-block" href="?edit" title="Edit page (E)">EDIT PAGE</a>
+1 -1
View File
@@ -6,7 +6,7 @@ document.addEventListener("DOMContentLoaded", function () {
if (headings.length < 2) return; if (headings.length < 2) return;
var nav = document.createElement("nav"); var nav = document.createElement("nav");
nav.className = "toc"; nav.className = "toc panel panel-sidebar";
var header = document.createElement("div"); var header = document.createElement("div");
header.className = "panel-header"; header.className = "panel-header";
+31 -21
View File
@@ -70,6 +70,15 @@
dropdown.className = 'suggest-dropdown'; dropdown.className = 'suggest-dropdown';
host.appendChild(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 = { var state = {
results: [], results: [],
total: 0, total: 0,
@@ -102,49 +111,50 @@
dropdown.classList.remove('is-open'); dropdown.classList.remove('is-open');
return; return;
} }
var table = document.createElement('table');
table.className = 'data-table';
var tbody = document.createElement('tbody');
table.appendChild(tbody);
var tokens = tokenize(state.query); var tokens = tokenize(state.query);
if (state.results.length === 0) { if (state.results.length === 0) {
var empty = document.createElement('div'); var empty = makeRow('is-empty', false);
empty.className = 'suggest-row is-empty'; empty.td.textContent = 'No matches';
empty.textContent = 'No matches'; tbody.appendChild(empty.tr);
dropdown.appendChild(empty);
} else { } else {
state.results.forEach(function (r, i) { state.results.forEach(function (r, i) {
var row = document.createElement('button'); var row = makeRow('suggest-row', true);
row.type = 'button'; row.tr.setAttribute('data-idx', String(i));
row.className = 'suggest-row';
row.setAttribute('data-idx', String(i));
var nameEl = document.createElement('span'); var nameEl = document.createElement('span');
nameEl.className = 'suggest-name'; nameEl.className = 'suggest-name';
nameEl.innerHTML = highlight(r.name, tokens); nameEl.innerHTML = highlight(r.name, tokens);
var pathEl = document.createElement('span'); var pathEl = document.createElement('span');
pathEl.className = 'suggest-path'; pathEl.className = 'suggest-path';
pathEl.textContent = '/' + r.path; pathEl.textContent = '/' + r.path;
row.appendChild(nameEl); row.td.appendChild(nameEl);
row.appendChild(pathEl); row.td.appendChild(pathEl);
if (i === state.activeIdx) row.classList.add('is-active'); if (i === state.activeIdx) row.tr.classList.add('is-active');
row.addEventListener('mousedown', function (e) { row.tr.addEventListener('mousedown', function (e) {
// mousedown (not click) so the input doesn't blur-close // mousedown (not click) so the input doesn't blur-close
// the dropdown before the pick handler fires. // the dropdown before the pick handler fires.
e.preventDefault(); e.preventDefault();
pick(i); pick(i);
}); });
dropdown.appendChild(row); tbody.appendChild(row.tr);
}); });
if (opts.showFooter && state.total > state.results.length) { if (opts.showFooter && state.total > state.results.length) {
var footer = document.createElement('button'); var footer = makeRow('suggest-row suggest-footer', true);
footer.type = 'button'; footer.td.textContent = 'Show all ' + state.total + ' matches';
footer.className = 'suggest-row suggest-footer';
footer.textContent = 'Show all ' + state.total + ' matches';
var footerIdx = state.results.length; var footerIdx = state.results.length;
if (state.activeIdx === footerIdx) footer.classList.add('is-active'); if (state.activeIdx === footerIdx) footer.tr.classList.add('is-active');
footer.addEventListener('mousedown', function (e) { footer.tr.addEventListener('mousedown', function (e) {
e.preventDefault(); e.preventDefault();
pickFooter(); pickFooter();
}); });
dropdown.appendChild(footer); tbody.appendChild(footer.tr);
} }
} }
dropdown.appendChild(table);
dropdown.classList.add('is-open'); dropdown.classList.add('is-open');
} }
@@ -225,7 +235,7 @@
state.activeIdx = next; state.activeIdx = next;
render(); render();
// Keep the active row in view. // 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) { if (active && active.scrollIntoView) {
try { active.scrollIntoView({ block: 'nearest' }); } catch (e) {} try { active.scrollIntoView({ block: 'nearest' }); } catch (e) {}
} }
+368 -755
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -28,7 +28,7 @@
var container = document.createElement('div'); var container = document.createElement('div');
var treeEl = document.createElement('div'); var treeEl = document.createElement('div');
treeEl.className = 'tree-picker'; treeEl.className = 'tree-picker panel';
var selectedPathEl = document.createElement('div'); var selectedPathEl = document.createElement('div');
selectedPathEl.className = 'tree-selected-path muted'; selectedPathEl.className = 'tree-selected-path muted';
+5 -1
View File
@@ -71,7 +71,11 @@ func renderMarkdown(raw []byte) template.HTML {
if err := md.Convert(raw, &buf); err != nil { if err := md.Convert(raw, &buf); err != nil {
return "" return ""
} }
return template.HTML(rewriteTaskCheckboxes(buf.Bytes())) out := rewriteTaskCheckboxes(buf.Bytes())
// Goldmark emits a bare `<table>`; tag it so it picks up the shared
// .data-table styling with the grid modifier (per-cell borders + header).
out = bytes.ReplaceAll(out, []byte("<table>"), []byte(`<table class="data-table data-table-grid">`))
return template.HTML(out)
} }
// extractFirstHeading returns the text of the first ATX heading in raw markdown, // extractFirstHeading returns the text of the first ATX heading in raw markdown,