Org html: revamp TOC

This commit is contained in:
TEC 2020-07-17 23:20:09 +08:00
parent b702523770
commit 0c93a6b858
3 changed files with 241 additions and 102 deletions

View File

@ -4778,6 +4778,40 @@ directly to the ~table~ element, so we have to wrap it in a ~div~.
(funcall orig-fn table contents info)
"</div>"))
#+END_SRC
***** TOC as a collapsable tree
The TOC is much nicer to navigate as a collapsable tree. Unfortunately we cannot
achieve this with CSS alone. Thankfully we can avoid JS though, by adapting the
TOC generation code to use a ~label~ for each item, and a hidden ~checkbox~ to keep
track of state.
To add this, we need to change one line in [[file:~/.emacs.d/.local/straight/repos/org-mode/lisp/ox-html.el::(format "<a href=\"#%s\">%s</a>"][org-html--format-toc-headline]].
Since we can actually accomplish the desired effect by adding advice /around/ the
function, without overriding it --- let's do that to reduce the bug surface of
this config a tad.
#+BEGIN_SRC emacs-lisp
(defadvice! org-html--format-toc-headline-colapseable (orig-fn headline info)
"Add a label and checkbox to `org-html--format-toc-headline's usual output,
to allow the TOC to be a collapseable tree."
:around #'org-html--format-toc-headline
(let ((id (or (org-element-property :CUSTOM_ID headline)
(org-export-get-reference headline info))))
(format "<input type='checkbox' id='toc--%s'/><label for='toc--%s'>%s</label>"
id id (funcall orig-fn headline info))))
#+END_SRC
Now, leaves (headings with no children) shouldn't have the ~label~ item. The
obvious way to achieve this is by including some /if no children.../ logic in
~org-html--format-toc-headline-colapseable~. Unfortunately, I can't my elisp isn't
up to par to extract the number of child headings from the mountain of info that
org provides.
#+BEGIN_SRC emacs-lisp
(defadvice! org-html--toc-text-stripped-leaves (orig-fn toc-entries)
"Remove label"
:around #'org-html--toc-text
(replace-regexp-in-string "<input [^>]+><label [^>]+>\\(.+?\\)</label></li>" "\\1</li>"
(funcall orig-fn toc-entries)))
#+END_SRC
***** Make verbatim different to code
Since we have =verbatim= and ~code~, let's use =verbatim= for key strokes.
#+BEGIN_SRC emacs-lisp

View File

@ -43,15 +43,6 @@ nav#table-of-contents {
}
}
&:hover {
#text-table-of-contents {
display: block;
}
h2 {
display: none;
}
}
h2 {
transform: rotate(-90deg);
position: relative;
@ -69,71 +60,135 @@ nav#table-of-contents {
//
::-webkit-scrollbar { width: 10px; height: 8px; }
::-webkit-scrollbar-track { background:transparent; }
::-webkit-scrollbar-thumb { background:transparent; border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background:transparent; }
::-webkit-scrollbar-track { background:#9992; }
::-webkit-scrollbar-thumb { background:#ccc; }
::-webkit-scrollbar-thumb:hover { background:#888; }
@media (min-width: 1280px) {
#table-of-contents {
position: fixed;
right: 4rem;
width: 18rem;
right: 1rem;
top: 0;
padding: 1em;
width: 15rem;
line-height: 1.5;
margin-top: 4rem;
h2 {
margin-top: 0;
}
> div > ul {
list-style: none;
padding: 0;
margin: 0;
max-height: calc(100vh - 5rem - 40px);
overflow-y: auto;
overflow-x: visible;
scrollbar-color: transparent transparent;
scrollbar-width: thin;
ul {
padding-left: 2em;
#text-table-of-contents {
position: relative;
&::before, &::after {
position: absolute;
content: '';
width: calc(100% - 10px);
height: 0.7rem;
left: 0;
z-index: 1;
}
> li {
display: block;
&::before {
top: 0;
background: linear-gradient(180deg, $back-light 0%, $back-light 35%, rgba(0,0,0,0) 100%);
}
&::after {
bottom: 0;
background: linear-gradient(0deg, $back-light 0%, $back-light 35%, rgba(0,0,0,0) 100%);
}
> ul {
list-style: none;
padding: 0;
margin: 0;
max-height: calc(100vh - 5rem - 50px);
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
ul {
padding-left: 2em;
}
ul.active {
display: inline-block;
}
li.active > ul {
display: inline-block;
}
li.active > label a, li.active > a {
color: $text-dark;
}
li.active > input:not(:checked) ~ label::after {
transform: rotate(90deg);
top: 5px;
opacity: 0.35;
}
> li:last-child {
margin-bottom: 2rem;
}
}
}
ul.active > li {
display: block;
}
li {
display: none;
a {
display: inline-block;
color: $text-light;
text-decoration: none;
-webkit-transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
// negating earlir styles
text-shadow: none;
background: none !important;
}
}
li:hover, li:hover > ul > li {
display: block !important;
a {
color: $text-gray;
}
}
li.active > a {
color: $text-dark;
}
li::before {
content: "";
}
}
}
#table-of-contents {
// width: 20rem;
// right: 1rem;
@media (min-width: 1440px) {
width: 20rem;
right: 2rem;
}
@media (min-width: 1640px) {
right: 5rem;
}
@media (min-width: 2000px) {
width: 25rem;
}
}
@media (min-width: 1640px) {
#table-of-contents {
right: 10rem;
#table-of-contents {
#text-table-of-contents {
> ul ul {
display: none;
}
li {
input[type=checkbox] {
display: none;
}
label {
display: inline-block;
width: 100%;
position: relative;
}
a {
display: inline-block;
color: $text-light;
text-decoration: none !important;
// negating other <a> styles
text-shadow: none;
background: none !important;
}
label::after {
content: "\25b6";
color: $text-light;
margin-left: 0.5em;
font-size: 10px;
display: inline-block;
position: absolute;
top: 3.4px;
left: -20px;
opacity: 0.8;
}
input:checked ~ ul {
display: inline-block !important; // needs to override more deeper selector div > ul ul
}
input:checked ~ label {
a {
font-weight: bold;
}
&::after {
transform: rotate(90deg);
top: 5px;
}
}
&::before {
content: "" !important;
}
}
}
}

View File

@ -2373,10 +2373,6 @@ nav#table-of-contents {
overflow: auto; }
nav#table-of-contents #text-table-of-contents ul {
margin: 0; }
nav#table-of-contents:hover #text-table-of-contents {
display: block; }
nav#table-of-contents:hover h2 {
display: none; }
nav#table-of-contents h2 {
transform: rotate(-90deg);
position: relative;
@ -2389,63 +2385,117 @@ nav#table-of-contents {
height: 8px; }
::-webkit-scrollbar-track {
background: transparent; }
background: #9992; }
::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 10px; }
background: #ccc; }
::-webkit-scrollbar-thumb:hover {
background: transparent; }
background: #888; }
@media (min-width: 1280px) {
#table-of-contents {
position: fixed;
right: 4rem;
width: 18rem;
right: 1rem;
top: 0;
padding: 1em;
width: 15rem;
line-height: 1.5;
margin-top: 4rem; }
#table-of-contents h2 {
margin-top: 0; }
#table-of-contents > div > ul {
list-style: none;
padding: 0;
margin: 0;
max-height: calc(100vh - 5rem - 40px);
overflow-y: auto;
overflow-x: visible;
scrollbar-color: transparent transparent;
scrollbar-width: thin; }
#table-of-contents > div > ul ul {
padding-left: 2em; }
#table-of-contents > div > ul > li {
display: block; }
#table-of-contents ul.active > li {
display: block; }
#table-of-contents li {
display: none; }
#table-of-contents li a {
display: inline-block;
color: var(--text-light);
text-decoration: none;
-webkit-transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
text-shadow: none;
background: none !important; }
#table-of-contents li:hover, #table-of-contents li:hover > ul > li {
display: block !important; }
#table-of-contents li:hover a, #table-of-contents li:hover > ul > li a {
color: var(--text-gray); }
#table-of-contents li.active > a {
color: var(--text-dark); }
#table-of-contents li::before {
content: ""; } }
#table-of-contents #text-table-of-contents {
position: relative; }
#table-of-contents #text-table-of-contents::before, #table-of-contents #text-table-of-contents::after {
position: absolute;
content: '';
width: calc(100% - 10px);
height: 0.7rem;
left: 0;
z-index: 1; }
#table-of-contents #text-table-of-contents::before {
top: 0;
background: linear-gradient(180deg, var(--back-light) 0%, var(--back-light) 35%, rgba(0, 0, 0, 0) 100%); }
#table-of-contents #text-table-of-contents::after {
bottom: 0;
background: linear-gradient(0deg, var(--back-light) 0%, var(--back-light) 35%, rgba(0, 0, 0, 0) 100%); }
#table-of-contents #text-table-of-contents > ul {
list-style: none;
padding: 0;
margin: 0;
max-height: calc(100vh - 5rem - 50px);
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin; }
#table-of-contents #text-table-of-contents > ul ul {
padding-left: 2em; }
#table-of-contents #text-table-of-contents > ul ul.active {
display: inline-block; }
#table-of-contents #text-table-of-contents > ul li.active > ul {
display: inline-block; }
#table-of-contents #text-table-of-contents > ul li.active > label a, #table-of-contents #text-table-of-contents > ul li.active > a {
color: var(--text-dark); }
#table-of-contents #text-table-of-contents > ul li.active > input:not(:checked) ~ label::after {
transform: rotate(90deg);
top: 5px;
opacity: 0.35; }
#table-of-contents #text-table-of-contents > ul > li:last-child {
margin-bottom: 2rem; } }
@media (min-width: 1440px) {
#table-of-contents {
width: 20rem;
right: 2rem; } }
@media (min-width: 1640px) {
#table-of-contents {
right: 10rem; } }
right: 5rem; } }
@media (min-width: 2000px) {
#table-of-contents {
width: 25rem; } }
#table-of-contents #text-table-of-contents > ul ul {
display: none; }
#table-of-contents #text-table-of-contents li input[type=checkbox] {
display: none; }
#table-of-contents #text-table-of-contents li label {
display: inline-block;
width: 100%;
position: relative; }
#table-of-contents #text-table-of-contents li a {
display: inline-block;
color: var(--text-light);
text-decoration: none !important;
text-shadow: none;
background: none !important; }
#table-of-contents #text-table-of-contents li label::after {
content: "\25b6";
color: var(--text-light);
margin-left: 0.5em;
font-size: 10px;
display: inline-block;
position: absolute;
top: 3.4px;
left: -20px;
opacity: 0.8; }
#table-of-contents #text-table-of-contents li input:checked ~ ul {
display: inline-block !important; }
#table-of-contents #text-table-of-contents li input:checked ~ label a {
font-weight: bold; }
#table-of-contents #text-table-of-contents li input:checked ~ label::after {
transform: rotate(90deg);
top: 5px; }
#table-of-contents #text-table-of-contents li::before {
content: "" !important; }
#breadcrumbs {
font-family: "Open Sans";