mirror of
https://github.com/thegeneralist01/archivr
synced 2026-07-21 18:55:36 +02:00
fix(frontend): make child entry rows interactive
ChildRow now receives onRowClick and selectedUids from EntryRow (which receives selectedUids from EntriesView alongside the existing booleans). Clicking a child row invokes onRowClick(child, e) so it flows through handleRowClick → selectEntry → fetchEntryDetail exactly as a root entry would. Shift-range selection gracefully degrades to single-select since child entries are not in the root entries array. Selected/multi-selected visual state is wired: .child-entry-row.is-selected shows the same #eee2d2 background + accent outline as root rows; hover restores full opacity. Frontend static assets rebuilt.
This commit is contained in:
parent
08087a4d6b
commit
9c1d416463
9 changed files with 86 additions and 106 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
47
crates/archivr-server/static/assets/index-DILm4nQE.js
Normal file
47
crates/archivr-server/static/assets/index-DILm4nQE.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -4,8 +4,8 @@
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Archivr</title>
|
||||
<script type="module" crossorigin src="/assets/index-YmIQCrug.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DLdY9nrw.css">
|
||||
<script type="module" crossorigin src="/assets/index-DILm4nQE.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Ba3TFwml.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export default function EntriesView({ entries, selectedUids, onRowClick, archive
|
|||
isSelected={selectedUids.size === 1 && selectedUids.has(entry.entry_uid)}
|
||||
isMultiSelected={selectedUids.size >= 2 && selectedUids.has(entry.entry_uid)}
|
||||
onRowClick={onRowClick}
|
||||
selectedUids={selectedUids}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,24 @@ import { useState } from 'react';
|
|||
import { formatTimestamp, formatBytes, valueText, sourceIconSvg } from '../utils';
|
||||
import { fetchEntryChildren } from '../api';
|
||||
|
||||
function ChildRow({ entry }) {
|
||||
function ChildRow({ entry, onRowClick, selectedUids }) {
|
||||
const isSelected = (selectedUids?.size === 1) && selectedUids.has(entry.entry_uid);
|
||||
const isMultiSelected = (selectedUids?.size >= 2) && selectedUids.has(entry.entry_uid);
|
||||
|
||||
const cls = ['child-entry-row',
|
||||
isSelected && 'is-selected',
|
||||
isMultiSelected && 'is-multi-selected',
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
return (
|
||||
<div className="child-entry-row">
|
||||
<div
|
||||
className={cls}
|
||||
tabIndex={0}
|
||||
data-entry-uid={entry.entry_uid}
|
||||
onMouseDown={e => { if (e.shiftKey) e.preventDefault(); }}
|
||||
onClick={e => onRowClick(entry, e)}
|
||||
onKeyDown={e => { if (e.key === 'Enter') onRowClick(entry, e); }}
|
||||
>
|
||||
<div className="col-check" aria-hidden="true" />
|
||||
<div className="col-added">{formatTimestamp(entry.archived_at)}</div>
|
||||
<div className="col-title">
|
||||
|
|
@ -24,7 +39,7 @@ function ChildRow({ entry }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default function EntryRow({ entry, archiveId, isSelected, isMultiSelected, onRowClick }) {
|
||||
export default function EntryRow({ entry, archiveId, isSelected, isMultiSelected, onRowClick, selectedUids }) {
|
||||
const [favFailed, setFavFailed] = useState(false);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [children, setChildren] = useState(null);
|
||||
|
|
@ -78,9 +93,6 @@ export default function EntryRow({ entry, archiveId, isSelected, isMultiSelected
|
|||
}
|
||||
}
|
||||
|
||||
// Outer wrapper: display:block so child-entries lives below the flex row.
|
||||
// One wrapper per entry → nth-child striping stays correct.
|
||||
// is-selected/is-multi-selected go here so #entries-body > div.is-selected still matches.
|
||||
const outerClass = [
|
||||
'entry-row-outer',
|
||||
isSelected && 'is-selected',
|
||||
|
|
@ -89,7 +101,6 @@ export default function EntryRow({ entry, archiveId, isSelected, isMultiSelected
|
|||
|
||||
return (
|
||||
<div className={outerClass} data-entry-uid={entry.entry_uid}>
|
||||
{/* entry-row-main is the interactive flex row */}
|
||||
<div
|
||||
className="entry-row-main"
|
||||
tabIndex={0}
|
||||
|
|
@ -142,7 +153,12 @@ export default function EntryRow({ entry, archiveId, isSelected, isMultiSelected
|
|||
<div className="child-entries" aria-label={`${entry.child_count} child entries`}>
|
||||
{childrenLoading && <div className="child-entries-loading">Loading…</div>}
|
||||
{children && children.map(child => (
|
||||
<ChildRow key={child.entry_uid} entry={child} />
|
||||
<ChildRow
|
||||
key={child.entry_uid}
|
||||
entry={child}
|
||||
onRowClick={onRowClick}
|
||||
selectedUids={selectedUids}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -2796,7 +2796,18 @@ body.has-audio-bar { padding-bottom: 56px; }
|
|||
border-bottom: 1px solid var(--line-soft);
|
||||
opacity: 0.88;
|
||||
}
|
||||
.child-entry-row:hover { opacity: 1; }
|
||||
.child-entry-row:last-child { border-bottom: none; }
|
||||
.child-entry-row.is-selected {
|
||||
background: #eee2d2;
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: -2px;
|
||||
opacity: 1;
|
||||
}
|
||||
.child-entry-row.is-multi-selected {
|
||||
background: #eee2d2;
|
||||
opacity: 1;
|
||||
}
|
||||
.child-entry-row > div {
|
||||
padding: 6px 10px;
|
||||
flex-shrink: 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue