mirror of
https://github.com/zadam/trilium.git
synced 2025-10-19 22:58:52 +02:00
feat(collection/presentation): add button to edit slide
This commit is contained in:
parent
8a85edf2db
commit
502e9b86bc
@ -438,4 +438,22 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openInCurrentNoteContext(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent | React.PointerEvent<HTMLCanvasElement> | null, notePath: string, viewScope?: ViewScope) {
|
||||||
|
const ntxId = $(evt?.target as any)
|
||||||
|
.closest("[data-ntx-id]")
|
||||||
|
.attr("data-ntx-id");
|
||||||
|
|
||||||
|
const noteContext = ntxId ? appContext.tabManager.getNoteContextById(ntxId) : appContext.tabManager.getActiveContext();
|
||||||
|
|
||||||
|
if (noteContext) {
|
||||||
|
noteContext.setNote(notePath, { viewScope }).then(() => {
|
||||||
|
if (noteContext !== appContext.tabManager.getActiveContext()) {
|
||||||
|
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
appContext.tabManager.openContextWithNote(notePath, { viewScope, activate: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default NoteContext;
|
export default NoteContext;
|
||||||
|
@ -4,6 +4,7 @@ import appContext, { type NoteCommandData } from "../components/app_context.js";
|
|||||||
import froca from "./froca.js";
|
import froca from "./froca.js";
|
||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import { ALLOWED_PROTOCOLS } from "@triliumnext/commons";
|
import { ALLOWED_PROTOCOLS } from "@triliumnext/commons";
|
||||||
|
import { openInCurrentNoteContext } from "../components/note_context.js";
|
||||||
|
|
||||||
function getNotePathFromUrl(url: string) {
|
function getNotePathFromUrl(url: string) {
|
||||||
const notePathMatch = /#(root[A-Za-z0-9_/]*)$/.exec(url);
|
const notePathMatch = /#(root[A-Za-z0-9_/]*)$/.exec(url);
|
||||||
@ -316,21 +317,7 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
|
|||||||
viewScope
|
viewScope
|
||||||
});
|
});
|
||||||
} else if (isLeftClick) {
|
} else if (isLeftClick) {
|
||||||
const ntxId = $(evt?.target as any)
|
openInCurrentNoteContext(evt, notePath, viewScope);
|
||||||
.closest("[data-ntx-id]")
|
|
||||||
.attr("data-ntx-id");
|
|
||||||
|
|
||||||
const noteContext = ntxId ? appContext.tabManager.getNoteContextById(ntxId) : appContext.tabManager.getActiveContext();
|
|
||||||
|
|
||||||
if (noteContext) {
|
|
||||||
noteContext.setNote(notePath, { viewScope }).then(() => {
|
|
||||||
if (noteContext !== appContext.tabManager.getActiveContext()) {
|
|
||||||
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
appContext.tabManager.openContextWithNote(notePath, { viewScope, activate: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (hrefLink) {
|
} else if (hrefLink) {
|
||||||
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
||||||
|
@ -9,6 +9,7 @@ import ShadowDom from "../../react/ShadowDom";
|
|||||||
import ActionButton from "../../react/ActionButton";
|
import ActionButton from "../../react/ActionButton";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { RefObject } from "preact";
|
import { RefObject } from "preact";
|
||||||
|
import { openInCurrentNoteContext } from "../../../components/note_context";
|
||||||
|
|
||||||
const stylesheets = [
|
const stylesheets = [
|
||||||
slideBaseStylesheet,
|
slideBaseStylesheet,
|
||||||
@ -19,6 +20,7 @@ const stylesheets = [
|
|||||||
export default function PresentationView({ note }: ViewModeProps<{}>) {
|
export default function PresentationView({ note }: ViewModeProps<{}>) {
|
||||||
const [ presentation, setPresentation ] = useState<PresentationModel>();
|
const [ presentation, setPresentation ] = useState<PresentationModel>();
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const apiRef = useRef<Reveal.Api>(null);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
buildPresentationModel(note).then(setPresentation);
|
buildPresentationModel(note).then(setPresentation);
|
||||||
@ -31,29 +33,41 @@ export default function PresentationView({ note }: ViewModeProps<{}>) {
|
|||||||
containerRef={containerRef}
|
containerRef={containerRef}
|
||||||
>
|
>
|
||||||
{stylesheets.map(stylesheet => <style>{stylesheet}</style>)}
|
{stylesheets.map(stylesheet => <style>{stylesheet}</style>)}
|
||||||
<Presentation presentation={presentation} />
|
<Presentation presentation={presentation} apiRef={apiRef} />
|
||||||
</ShadowDom>
|
</ShadowDom>
|
||||||
<ButtonOverlay containerRef={containerRef} />
|
<ButtonOverlay containerRef={containerRef} apiRef={apiRef} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ButtonOverlay({ containerRef }: { containerRef: RefObject<HTMLDivElement> }) {
|
function ButtonOverlay({ containerRef, apiRef }: { containerRef: RefObject<HTMLDivElement>, apiRef: RefObject<Reveal.Api> }) {
|
||||||
return (
|
return (
|
||||||
<div className="presentation-button-bar">
|
<div className="presentation-button-bar">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon="bx bx-fullscreen" text="Start presentation"
|
icon="bx bx-edit"
|
||||||
onClick={() => {
|
text="Edit this slide"
|
||||||
containerRef.current?.requestFullscreen();
|
onClick={e => {
|
||||||
|
const currentSlide = apiRef.current?.getCurrentSlide();
|
||||||
|
const noteId = getNoteIdFromSlide(currentSlide);
|
||||||
|
|
||||||
|
if (noteId) {
|
||||||
|
openInCurrentNoteContext(e, noteId);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ActionButton
|
||||||
|
icon="bx bx-fullscreen"
|
||||||
|
text="Start presentation"
|
||||||
|
onClick={() => containerRef.current?.requestFullscreen()}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Presentation({ presentation } : { presentation: PresentationModel }) {
|
function Presentation({ presentation, apiRef: externalApiRef } : { presentation: PresentationModel, apiRef: RefObject<Reveal.Api> }) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const apiRef = useRef<Reveal.Api | null>(null);
|
const apiRef = useRef<Reveal.Api>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (apiRef.current || !containerRef.current) return;
|
if (apiRef.current || !containerRef.current) return;
|
||||||
@ -70,8 +84,9 @@ function Presentation({ presentation } : { presentation: PresentationModel }) {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
externalApiRef.current = apiRef.current;
|
||||||
apiRef.current.initialize().then(() => {
|
apiRef.current.initialize().then(() => {
|
||||||
console.log("Slide.js initialized.");
|
// Initialization logic.
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -97,16 +112,21 @@ function Presentation({ presentation } : { presentation: PresentationModel }) {
|
|||||||
function Slide({ slide }: { slide: PresentationSlideModel }) {
|
function Slide({ slide }: { slide: PresentationSlideModel }) {
|
||||||
if (!slide.verticalSlides) {
|
if (!slide.verticalSlides) {
|
||||||
// Normal slide.
|
// Normal slide.
|
||||||
return <section dangerouslySetInnerHTML={slide.content} />;
|
return <section data-note-id={slide.noteId} dangerouslySetInnerHTML={slide.content} />;
|
||||||
} else {
|
} else {
|
||||||
// Slide with sub notes (show as vertical slides).
|
// Slide with sub notes (show as vertical slides).
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<section dangerouslySetInnerHTML={slide.content} />
|
<section data-note-id={slide.noteId} dangerouslySetInnerHTML={slide.content} />
|
||||||
{slide.verticalSlides.map((slide) => (
|
{slide.verticalSlides.map((slide) => (
|
||||||
<section dangerouslySetInnerHTML={slide.content} />
|
<section data-note-id={slide.noteId} dangerouslySetInnerHTML={slide.content} />
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNoteIdFromSlide(slide: HTMLElement | undefined) {
|
||||||
|
if (!slide) return;
|
||||||
|
return slide.dataset.noteId;
|
||||||
|
}
|
||||||
|
@ -2,12 +2,14 @@ import FNote from "../../../entities/fnote";
|
|||||||
|
|
||||||
type DangerouslySetInnerHTML = { __html: string; };
|
type DangerouslySetInnerHTML = { __html: string; };
|
||||||
|
|
||||||
export interface PresentationSlideModel {
|
/** A top-level slide with optional vertical slides. */
|
||||||
content: DangerouslySetInnerHTML;
|
export interface PresentationSlideModel extends PresentationSlideBaseModel {
|
||||||
verticalSlides: PresentationVerticalSlideModel[] | undefined;
|
verticalSlides: PresentationSlideBaseModel[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PresentationVerticalSlideModel {
|
/** Either a top-level slide or a vertical slide. */
|
||||||
|
interface PresentationSlideBaseModel {
|
||||||
|
noteId: string;
|
||||||
content: DangerouslySetInnerHTML;
|
content: DangerouslySetInnerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +24,7 @@ export async function buildPresentationModel(note: FNote): Promise<PresentationM
|
|||||||
|
|
||||||
for (const slideNote of slideNotes) {
|
for (const slideNote of slideNotes) {
|
||||||
slides.push({
|
slides.push({
|
||||||
|
noteId: slideNote.noteId,
|
||||||
content: processContent(await slideNote.getContent() ?? ""),
|
content: processContent(await slideNote.getContent() ?? ""),
|
||||||
verticalSlides: await buildVerticalSlides(slideNote)
|
verticalSlides: await buildVerticalSlides(slideNote)
|
||||||
})
|
})
|
||||||
@ -30,13 +33,14 @@ export async function buildPresentationModel(note: FNote): Promise<PresentationM
|
|||||||
return { slides };
|
return { slides };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildVerticalSlides(parentSlideNote: FNote): Promise<undefined | PresentationVerticalSlideModel[]> {
|
async function buildVerticalSlides(parentSlideNote: FNote): Promise<undefined | PresentationSlideBaseModel[]> {
|
||||||
const children = await parentSlideNote.getChildNotes();
|
const children = await parentSlideNote.getChildNotes();
|
||||||
if (!children.length) return;
|
if (!children.length) return;
|
||||||
|
|
||||||
const slides: PresentationVerticalSlideModel[] = [];
|
const slides: PresentationSlideBaseModel[] = [];
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
slides.push({
|
slides.push({
|
||||||
|
noteId: child.noteId,
|
||||||
content: processContent(await child.getContent())
|
content: processContent(await child.getContent())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user