mirror of
https://github.com/zadam/trilium.git
synced 2025-11-11 08:58:58 +01:00
chore(react/type_widget): list attachments with content
This commit is contained in:
parent
58b14ae31c
commit
dc73467d34
@ -11,89 +11,8 @@ import type FAttachment from "../entities/fattachment.js";
|
||||
import type { EventData } from "../components/app_context.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="attachment-detail-widget">
|
||||
<style>
|
||||
.attachment-detail-widget {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.attachment-title-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.attachment-details {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper .rendered-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper pre {
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.list-view .attachment-content-wrapper {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.full-detail {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.full-detail .attachment-content-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.list-view .attachment-content-wrapper pre {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper img {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.list-view .attachment-content-wrapper img, .attachment-detail-wrapper.list-view .attachment-content-wrapper video {
|
||||
max-height: 300px;
|
||||
max-width: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.full-detail .attachment-content-wrapper img {
|
||||
max-width: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.scheduled-for-deletion .attachment-content-wrapper img {
|
||||
filter: contrast(10%);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="attachment-detail-wrapper">
|
||||
<div class="attachment-title-line">
|
||||
<div class="attachment-actions-container"></div>
|
||||
<h4 class="attachment-title"></h4>
|
||||
<div class="attachment-details"></div>
|
||||
<div style="flex: 1 1;"></div>
|
||||
</div>
|
||||
|
||||
<div class="attachment-deletion-warning alert alert-info" style="margin-top: 15px;"></div>
|
||||
|
||||
<div class="attachment-content-wrapper"></div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
@ -125,21 +44,6 @@ export default class AttachmentDetailWidget extends BasicWidget {
|
||||
this.$wrapper = this.$widget.find(".attachment-detail-wrapper");
|
||||
this.$wrapper.addClass(this.isFullDetail ? "full-detail" : "list-view");
|
||||
|
||||
if (!this.isFullDetail) {
|
||||
const $link = await linkService.createLink(this.attachment.ownerId, {
|
||||
title: this.attachment.title,
|
||||
viewScope: {
|
||||
viewMode: "attachments",
|
||||
attachmentId: this.attachment.attachmentId
|
||||
}
|
||||
});
|
||||
$link.addClass("use-tn-links");
|
||||
|
||||
this.$wrapper.find(".attachment-title").append($link);
|
||||
} else {
|
||||
this.$wrapper.find(".attachment-title").text(this.attachment.title);
|
||||
}
|
||||
|
||||
const $deletionWarning = this.$wrapper.find(".attachment-deletion-warning");
|
||||
const { utcDateScheduledForErasureSince } = this.attachment;
|
||||
|
||||
@ -166,10 +70,9 @@ export default class AttachmentDetailWidget extends BasicWidget {
|
||||
$deletionWarning.hide();
|
||||
}
|
||||
|
||||
this.$wrapper.find(".attachment-details").text(t("attachment_detail_2.role_and_size", { role: this.attachment.role, size: utils.formatSize(this.attachment.contentLength) }));
|
||||
this.$wrapper.find(".attachment-actions-container").append(this.attachmentActionsWidget.render());
|
||||
|
||||
const { $renderedContent } = await contentRenderer.getRenderedContent(this.attachment, { imageHasZoom: this.isFullDetail });
|
||||
const { $renderedContent } = await );
|
||||
this.$wrapper.find(".attachment-content-wrapper").append($renderedContent);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
import link from "../../services/link";
|
||||
import link, { ViewScope } from "../../services/link";
|
||||
import { useImperativeSearchHighlighlighting } from "./hooks";
|
||||
|
||||
interface NoteLinkOpts {
|
||||
@ -11,18 +11,25 @@ interface NoteLinkOpts {
|
||||
noPreview?: boolean;
|
||||
noTnLink?: boolean;
|
||||
highlightedTokens?: string[] | null | undefined;
|
||||
// Override the text of the link, otherwise the note title is used.
|
||||
title?: string;
|
||||
viewScope?: ViewScope;
|
||||
}
|
||||
|
||||
export default function NoteLink({ className, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens }: NoteLinkOpts) {
|
||||
export default function NoteLink({ className, notePath, showNotePath, showNoteIcon, style, noPreview, noTnLink, highlightedTokens, title, viewScope }: NoteLinkOpts) {
|
||||
const stringifiedNotePath = Array.isArray(notePath) ? notePath.join("/") : notePath;
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
const [ jqueryEl, setJqueryEl ] = useState<JQuery<HTMLElement>>();
|
||||
const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens);
|
||||
|
||||
useEffect(() => {
|
||||
link.createLink(stringifiedNotePath, { showNotePath, showNoteIcon })
|
||||
.then(setJqueryEl);
|
||||
}, [ stringifiedNotePath, showNotePath ]);
|
||||
link.createLink(stringifiedNotePath, {
|
||||
title,
|
||||
showNotePath,
|
||||
showNoteIcon,
|
||||
viewScope
|
||||
}).then(setJqueryEl);
|
||||
}, [ stringifiedNotePath, showNotePath, title, viewScope ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current || !jqueryEl) return;
|
||||
|
||||
@ -12,3 +12,74 @@
|
||||
align-items: baseline;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region Attachment detail */
|
||||
.attachment-detail-widget {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.attachment-title-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.attachment-details {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper .rendered-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper pre {
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.list-view .attachment-content-wrapper {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.full-detail {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.full-detail .attachment-content-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.list-view .attachment-content-wrapper pre {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.attachment-content-wrapper img {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.list-view .attachment-content-wrapper img, .attachment-detail-wrapper.list-view .attachment-content-wrapper video {
|
||||
max-height: 300px;
|
||||
max-width: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.full-detail .attachment-content-wrapper img {
|
||||
max-width: 90%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.attachment-detail-wrapper.scheduled-for-deletion .attachment-content-wrapper img {
|
||||
filter: contrast(10%);
|
||||
}
|
||||
/* #endregion */
|
||||
@ -3,14 +3,36 @@ import { TypeWidgetProps } from "./type_widget";
|
||||
import "./Attachment.css";
|
||||
import NoteLink from "../react/NoteLink";
|
||||
import Button from "../react/Button";
|
||||
import { useContext } from "preact/hooks";
|
||||
import { useContext, useEffect, useRef, useState } from "preact/hooks";
|
||||
import { ParentComponent } from "../react/react_utils";
|
||||
import HelpButton from "../react/HelpButton";
|
||||
import FAttachment from "../../entities/fattachment";
|
||||
import Alert from "../react/Alert";
|
||||
import utils from "../../services/utils";
|
||||
import content_renderer from "../../services/content_renderer";
|
||||
|
||||
export function AttachmentList({ note }: TypeWidgetProps) {
|
||||
const [ attachments, setAttachments ] = useState<FAttachment[]>([]);
|
||||
|
||||
function refresh() {
|
||||
note.getAttachments().then(setAttachments);
|
||||
}
|
||||
|
||||
useEffect(refresh, [ note ]);
|
||||
|
||||
return (
|
||||
<div className="attachment-list note-detail-printable">
|
||||
<AttachmentListHeader noteId={note.noteId} />
|
||||
|
||||
<div className="attachment-list-wrapper">
|
||||
{attachments.length ? (
|
||||
attachments.map(attachment => <AttachmentDetail attachment={attachment} />)
|
||||
) : (
|
||||
<Alert type="info">
|
||||
{t("attachment_list.no_attachments")}
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -39,3 +61,42 @@ function AttachmentListHeader({ noteId }: { noteId: string }) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AttachmentDetail({ attachment, isFullDetail }: { attachment: FAttachment, isFullDetail: boolean }) {
|
||||
const contentWrapper = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
content_renderer.getRenderedContent(attachment, { imageHasZoom: isFullDetail })
|
||||
.then(({ $renderedContent }) => {
|
||||
contentWrapper.current?.replaceChildren(...$renderedContent);
|
||||
})
|
||||
}, [ attachment ]);
|
||||
|
||||
return (
|
||||
<div className="attachment-detail-widget">
|
||||
<div className="attachment-detail-wrapper">
|
||||
<div className="attachment-title-line">
|
||||
<div className="attachment-actions-container"></div>
|
||||
<h4 className="attachment-title">
|
||||
{!isFullDetail ? (
|
||||
<NoteLink
|
||||
notePath={attachment.ownerId}
|
||||
title={attachment.title}
|
||||
viewScope={{
|
||||
viewMode: "attachments",
|
||||
attachmentId: attachment.attachmentId
|
||||
}}
|
||||
/>
|
||||
) : (attachment.title)}
|
||||
</h4>
|
||||
<div className="attachment-details">
|
||||
{t("attachment_detail_2.role_and_size", { role: attachment.role, size: utils.formatSize(attachment.contentLength) })}
|
||||
</div>
|
||||
<div style="flex: 1 1;"></div>
|
||||
</div>
|
||||
|
||||
<div ref={contentWrapper} className="attachment-content-wrapper" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import { t } from "../../services/i18n.js";
|
||||
import type { EventData } from "../../components/app_context.js";
|
||||
|
||||
const TPL = /*html*/`
|
||||
<div class="attachment-list-wrapper"></div>
|
||||
`;
|
||||
|
||||
export default class AttachmentListTypeWidget extends TypeWidget {
|
||||
@ -27,28 +26,12 @@ export default class AttachmentListTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
async doRefresh(note: Parameters<TypeWidget["doRefresh"]>[0]) {
|
||||
const $helpButton = $(`
|
||||
<button class="attachment-help-button icon-action bx bx-help-circle"
|
||||
type="button" data-help-page="attachments.html"
|
||||
title="${}">
|
||||
</button>
|
||||
`);
|
||||
utils.initHelpButtons($helpButton);
|
||||
|
||||
const noteLink = await linkService.createLink(this.noteId); // do separately to avoid race condition between empty() and .append()
|
||||
noteLink.addClass("use-tn-links");
|
||||
|
||||
this.$list.empty();
|
||||
this.children = [];
|
||||
this.renderedAttachmentIds = new Set();
|
||||
|
||||
const attachments = await note.getAttachments();
|
||||
|
||||
if (attachments.length === 0) {
|
||||
this.$list.html('<div class="alert alert-info">' + t("attachment_list.no_attachments") + "</div>");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attachment of attachments) {
|
||||
const attachmentDetailWidget = new AttachmentDetailWidget(attachment, false);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user