Update Chapters list to make modifying a chapter take less scrolling.
This commit is contained in:
@@ -224,9 +224,9 @@ export function storiesPage(main, _params, config) {
|
||||
fill(
|
||||
main,
|
||||
h(
|
||||
'section',
|
||||
{ class: 'card' },
|
||||
h('h2', {}, 'Add stories'),
|
||||
'details',
|
||||
{ class: 'card', open: true },
|
||||
h('summary', {}, h('h2', {}, 'Add stories')),
|
||||
urls,
|
||||
h(
|
||||
'div',
|
||||
@@ -240,16 +240,18 @@ export function storiesPage(main, _params, config) {
|
||||
),
|
||||
),
|
||||
h(
|
||||
'section',
|
||||
{ class: 'card' },
|
||||
'details',
|
||||
{ class: 'card', open: true },
|
||||
h(
|
||||
'div',
|
||||
'summary',
|
||||
{ class: 'section-heading' },
|
||||
h('h2', {}, 'Stories'),
|
||||
h('input', {
|
||||
type: 'search',
|
||||
placeholder: 'Filter',
|
||||
'aria-label': 'Filter stories',
|
||||
// Prevents a click here from also toggling the details open or closed.
|
||||
onclick: (e) => e.preventDefault(),
|
||||
oninput: (e) => {
|
||||
filter = e.target.value;
|
||||
if (stories) render();
|
||||
@@ -258,7 +260,7 @@ export function storiesPage(main, _params, config) {
|
||||
),
|
||||
table,
|
||||
),
|
||||
h('section', { class: 'card' }, h('h2', {}, 'Jobs'), jobs.el),
|
||||
h('details', { class: 'card', open: true }, h('summary', {}, h('h2', {}, 'Jobs')), jobs.el),
|
||||
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Import an epub file')), epubImport()),
|
||||
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Import from the library')), libraryImport(config)),
|
||||
);
|
||||
|
||||
+25
-24
@@ -536,9 +536,14 @@ export function storyPage(main, { storyId }, config) {
|
||||
main,
|
||||
summary,
|
||||
h(
|
||||
'section',
|
||||
'details',
|
||||
{ class: 'card' },
|
||||
h('h2', {}, 'Download'),
|
||||
h(
|
||||
'summary',
|
||||
{ class: 'section-heading' },
|
||||
h('h2', {}, 'Download'),
|
||||
button('Start', start, { class: 'primary summary-action' }),
|
||||
),
|
||||
h('fieldset', {}, h('legend', {}, 'What to download'), modes, modeHint),
|
||||
h(
|
||||
'details',
|
||||
@@ -555,11 +560,10 @@ export function storyPage(main, { storyId }, config) {
|
||||
h('a', { download: true, href: apiUrl(`stories/${id}/epub`) }, 'Download ePub'),
|
||||
),
|
||||
),
|
||||
h('section', { class: 'card' }, h('h2', {}, 'Updates'), updates),
|
||||
h(
|
||||
'section',
|
||||
'details',
|
||||
{ class: 'card' },
|
||||
h('h2', {}, 'Metadata'),
|
||||
h('summary', {}, h('h2', {}, 'Metadata')),
|
||||
h(
|
||||
'p',
|
||||
{ class: 'hint' },
|
||||
@@ -570,10 +574,11 @@ export function storyPage(main, { storyId }, config) {
|
||||
),
|
||||
metadata,
|
||||
),
|
||||
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Checking for new chapters')), updates),
|
||||
h(
|
||||
'section',
|
||||
'details',
|
||||
{ class: 'card' },
|
||||
h('h2', {}, 'Replacement rules'),
|
||||
h('summary', {}, h('h2', {}, 'Replacement rules')),
|
||||
h(
|
||||
'p',
|
||||
{ class: 'hint' },
|
||||
@@ -588,30 +593,26 @@ export function storyPage(main, { storyId }, config) {
|
||||
preview.element,
|
||||
),
|
||||
h(
|
||||
'section',
|
||||
{ class: 'card' },
|
||||
'details',
|
||||
{ class: 'card', open: true },
|
||||
h('summary', {}, h('h2', {}, 'Chapters')),
|
||||
h(
|
||||
'div',
|
||||
{ class: 'section-heading' },
|
||||
h('h2', {}, 'Chapters'),
|
||||
h(
|
||||
'div',
|
||||
{ class: 'controls' },
|
||||
button('Recheck selected', () =>
|
||||
selected.size ? queue('recheck', { chapter_ids: [...selected] }) : toast('Select chapters first', true),
|
||||
),
|
||||
button('Take out of book', () => setIncluded(false)),
|
||||
button('Put back in book', () => setIncluded(true)),
|
||||
{ class: 'controls chapter-actions' },
|
||||
button('Recheck selected', () =>
|
||||
selected.size ? queue('recheck', { chapter_ids: [...selected] }) : toast('Select chapters first', true),
|
||||
),
|
||||
button('Take out of book', () => setIncluded(false)),
|
||||
button('Put back in book', () => setIncluded(true)),
|
||||
),
|
||||
chapters,
|
||||
),
|
||||
h('details', { class: 'card' }, h('summary', {}, h('h2', {}, 'Import chapters from an epub')), epubImport(id)),
|
||||
h('section', { class: 'card' }, h('h2', {}, 'Recent jobs'), jobs.el),
|
||||
h('details', { class: 'card', open: true }, h('summary', {}, h('h2', {}, 'Recent jobs')), jobs.el),
|
||||
h(
|
||||
'section',
|
||||
'details',
|
||||
{ class: 'card' },
|
||||
h('h2', {}, 'Library link'),
|
||||
h('summary', {}, h('h2', {}, 'Library link')),
|
||||
h(
|
||||
'p',
|
||||
{ class: 'hint' },
|
||||
@@ -637,9 +638,9 @@ export function storyPage(main, { storyId }, config) {
|
||||
searchResults,
|
||||
),
|
||||
h(
|
||||
'section',
|
||||
'details',
|
||||
{ class: 'card card-danger' },
|
||||
h('h2', {}, 'Stop tracking'),
|
||||
h('summary', {}, h('h2', {}, 'Stop tracking')),
|
||||
h('p', { class: 'hint' }, 'Deletes the stored chapters, revisions and rules for this story. The library book stays.'),
|
||||
button(
|
||||
'Stop tracking this story',
|
||||
|
||||
@@ -306,14 +306,47 @@ fieldset:has(+ .force-fields) {
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
details > summary h2 {
|
||||
display: inline;
|
||||
/* A card that folds shut, such as "Import chapters from an epub". Scoped to .card so it does not
|
||||
change the small "Site value differs" fold of a metadata field. */
|
||||
details.card > summary {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
details.card > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details.card > summary::before {
|
||||
content: '▸';
|
||||
flex: none;
|
||||
color: var(--muted);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
details.card[open] > summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
details.card > summary h2 {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
/* Without this, the h2's own bottom margin throws off the vertical centering of the
|
||||
arrow next to it, and leaves extra space under the heading. */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
details[open] > summary {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* The Download card offers this button even while closed. Once open, the controls below repeat it. */
|
||||
details.card[open] > summary .summary-action {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.controls,
|
||||
.section-heading,
|
||||
.account,
|
||||
@@ -339,6 +372,21 @@ details[open] > summary {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Stays visible while the chapter list scrolls, so the buttons need no scrolling back up to
|
||||
reach, but only once a chapter is selected. Otherwise it would cover the top of the list for
|
||||
no reason. It stops following once the Chapters card itself scrolls out of view. */
|
||||
.card:has(.chapters-table input:checked) .chapter-actions {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
background: var(--card-bg);
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
/* Without its own compositing layer, some phone browsers repaint a stuck element with
|
||||
several stacked children wrong while scrolling, showing only the last child. */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.login form,
|
||||
.login label:has(> input:not([type='checkbox'])) {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user