feat(client/print): render presentation without shadow DOM

This commit is contained in:
Elian Doran 2025-10-18 21:56:10 +03:00
parent e374b31a1c
commit f6d7ecab40
No known key found for this signature in database
8 changed files with 36 additions and 18 deletions

View File

@ -138,7 +138,7 @@ export default class DesktopLayout {
.child(new PromotedAttributesWidget()) .child(new PromotedAttributesWidget())
.child(<SqlTableSchemas />) .child(<SqlTableSchemas />)
.child(new NoteDetailWidget()) .child(new NoteDetailWidget())
.child(<NoteList />) .child(<NoteList media="screen" />)
.child(<SearchResult />) .child(<SearchResult />)
.child(<SqlResults />) .child(<SqlResults />)
.child(<ScrollPadding />) .child(<ScrollPadding />)

View File

@ -66,6 +66,6 @@ export function applyModals(rootContainer: RootContainer) {
.child(<PopupEditorFormattingToolbar />) .child(<PopupEditorFormattingToolbar />)
.child(new PromotedAttributesWidget()) .child(new PromotedAttributesWidget())
.child(new NoteDetailWidget()) .child(new NoteDetailWidget())
.child(<NoteList displayOnlyCollections />)) .child(<NoteList media="screen" displayOnlyCollections />))
.child(<CallToActionDialog />); .child(<CallToActionDialog />);
} }

View File

@ -154,7 +154,7 @@ export default class MobileLayout {
.filling() .filling()
.contentSized() .contentSized()
.child(new NoteDetailWidget()) .child(new NoteDetailWidget())
.child(<NoteList />) .child(<NoteList media="screen" />)
.child(<FilePropertiesWrapper />) .child(<FilePropertiesWrapper />)
) )
.child(<MobileEditorToolbar />) .child(<MobileEditorToolbar />)

View File

@ -32,6 +32,7 @@ function handleCollection(note: FNote) {
notePath={note.getBestNotePath().join("/")} notePath={note.getBestNotePath().join("/")}
ntxId="print" ntxId="print"
highlightedTokens={null} highlightedTokens={null}
media="print"
/> />
); );
} }

View File

@ -1,4 +1,4 @@
import { allViewTypes, ViewModeProps, ViewTypeOptions } from "./interface"; import { allViewTypes, ViewModeMedia, ViewModeProps, ViewTypeOptions } from "./interface";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks"; import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
import FNote from "../../entities/fnote"; import FNote from "../../entities/fnote";
import "./NoteList.css"; import "./NoteList.css";
@ -22,9 +22,10 @@ interface NoteListProps {
displayOnlyCollections?: boolean; displayOnlyCollections?: boolean;
isEnabled: boolean; isEnabled: boolean;
ntxId: string | null | undefined; ntxId: string | null | undefined;
media: ViewModeMedia;
} }
export default function NoteList<T extends object>(props: Pick<NoteListProps, "displayOnlyCollections">) { export default function NoteList<T extends object>(props: Pick<NoteListProps, "displayOnlyCollections" | "media">) {
const { note, noteContext, notePath, ntxId } = useNoteContext(); const { note, noteContext, notePath, ntxId } = useNoteContext();
const isEnabled = noteContext?.hasNoteList(); const isEnabled = noteContext?.hasNoteList();
return <CustomNoteList note={note} isEnabled={!!isEnabled} notePath={notePath} ntxId={ntxId} {...props} /> return <CustomNoteList note={note} isEnabled={!!isEnabled} notePath={notePath} ntxId={ntxId} {...props} />
@ -34,7 +35,7 @@ export function SearchNoteList<T extends object>(props: Omit<NoteListProps, "isE
return <CustomNoteList {...props} isEnabled={true} /> return <CustomNoteList {...props} isEnabled={true} />
} }
export function CustomNoteList<T extends object>({ note, isEnabled: shouldEnable, notePath, highlightedTokens, displayOnlyCollections, ntxId }: NoteListProps) { export function CustomNoteList<T extends object>({ note, isEnabled: shouldEnable, notePath, highlightedTokens, displayOnlyCollections, ntxId, ...restProps }: NoteListProps) {
const widgetRef = useRef<HTMLDivElement>(null); const widgetRef = useRef<HTMLDivElement>(null);
const viewType = useNoteViewType(note); const viewType = useNoteViewType(note);
const noteIds = useNoteIds(note, viewType, ntxId); const noteIds = useNoteIds(note, viewType, ntxId);
@ -76,7 +77,8 @@ export function CustomNoteList<T extends object>({ note, isEnabled: shouldEnable
note, noteIds, notePath, note, noteIds, notePath,
highlightedTokens, highlightedTokens,
viewConfig: viewModeConfig[0], viewConfig: viewModeConfig[0],
saveConfig: viewModeConfig[1] saveConfig: viewModeConfig[1],
...restProps
} }
} }

View File

@ -3,6 +3,8 @@ import FNote from "../../entities/fnote";
export const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board", "presentation"] as const; export const allViewTypes = ["list", "grid", "calendar", "table", "geoMap", "board", "presentation"] as const;
export type ViewTypeOptions = typeof allViewTypes[number]; export type ViewTypeOptions = typeof allViewTypes[number];
export type ViewModeMedia = "screen" | "print";
export interface ViewModeProps<T extends object> { export interface ViewModeProps<T extends object> {
note: FNote; note: FNote;
notePath: string; notePath: string;
@ -13,4 +15,5 @@ export interface ViewModeProps<T extends object> {
highlightedTokens: string[] | null | undefined; highlightedTokens: string[] | null | undefined;
viewConfig: T | undefined; viewConfig: T | undefined;
saveConfig(newConfig: T): void; saveConfig(newConfig: T): void;
media: ViewModeMedia;
} }

View File

@ -14,7 +14,7 @@ import { t } from "../../../services/i18n";
import { DEFAULT_THEME, loadPresentationTheme } from "./themes"; import { DEFAULT_THEME, loadPresentationTheme } from "./themes";
import FNote from "../../../entities/fnote"; import FNote from "../../../entities/fnote";
export default function PresentationView({ note, noteIds }: ViewModeProps<{}>) { export default function PresentationView({ note, noteIds, media }: ViewModeProps<{}>) {
const [ presentation, setPresentation ] = useState<PresentationModel>(); const [ presentation, setPresentation ] = useState<PresentationModel>();
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [ api, setApi ] = useState<Reveal.Api>(); const [ api, setApi ] = useState<Reveal.Api>();
@ -33,18 +33,28 @@ export default function PresentationView({ note, noteIds }: ViewModeProps<{}>) {
useLayoutEffect(refresh, [ note, noteIds ]); useLayoutEffect(refresh, [ note, noteIds ]);
return presentation && stylesheets && ( if (!presentation || !stylesheets) return;
const content = (
<>
{stylesheets.map(stylesheet => <style>{stylesheet}</style>)}
<Presentation presentation={presentation} setApi={setApi} />
</>
);
if (media === "screen") {
return (
<> <>
<ShadowDom <ShadowDom
className="presentation-container" className="presentation-container"
containerRef={containerRef} containerRef={containerRef}
> >{content}</ShadowDom>
{stylesheets.map(stylesheet => <style>{stylesheet}</style>)}
<Presentation presentation={presentation} setApi={setApi} />
</ShadowDom>
<ButtonOverlay containerRef={containerRef} api={api} /> <ButtonOverlay containerRef={containerRef} api={api} />
</> </>
) )
} else {
// Shadow DOM doesn't work well with Reveal.js's PDF printing mechanism.
return content;
}
} }
function usePresentationStylesheets(note: FNote) { function usePresentationStylesheets(note: FNote) {
@ -128,6 +138,7 @@ function Presentation({ presentation, setApi } : { presentation: PresentationMod
const api = new Reveal(containerRef.current, { const api = new Reveal(containerRef.current, {
transition: "slide", transition: "slide",
embedded: true, embedded: true,
pdfMaxPagesPerSlide: 1,
keyboardCondition(event) { keyboardCondition(event) {
// Full-screen requests sometimes fail, we rely on the UI button instead. // Full-screen requests sometimes fail, we rely on the UI button instead.
if (event.key === "f") { if (event.key === "f") {

View File

@ -54,6 +54,7 @@ export default function SearchResult() {
{state === SearchResultState.GOT_RESULTS && ( {state === SearchResultState.GOT_RESULTS && (
<SearchNoteList <SearchNoteList
media="screen"
note={note} note={note}
notePath={notePath} notePath={notePath}
highlightedTokens={highlightedTokens} highlightedTokens={highlightedTokens}