Skip to content

Commit 455dc5d

Browse files
committed
feat(layout): automatically collapse promoted attributes in full-height notes
1 parent 158f5ac commit 455dc5d

File tree

3 files changed

+51
-26
lines changed

3 files changed

+51
-26
lines changed

apps/client/src/widgets/NoteDetail.tsx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import { useNoteContext, useTriliumEvent } from "./react/hooks"
2-
import FNote from "../entities/fnote";
3-
import protected_session_holder from "../services/protected_session_holder";
1+
import "./NoteDetail.css";
2+
3+
import { isValidElement, VNode } from "preact";
44
import { useEffect, useRef, useState } from "preact/hooks";
5+
56
import NoteContext from "../components/note_context";
6-
import { isValidElement, VNode } from "preact";
7-
import { TypeWidgetProps } from "./type_widgets/type_widget";
8-
import "./NoteDetail.css";
7+
import FNote from "../entities/fnote";
98
import attributes from "../services/attributes";
10-
import { ExtendedNoteType, TYPE_MAPPINGS, TypeWidget } from "./note_types";
11-
import { dynamicRequire, isElectron, isMobile } from "../services/utils";
12-
import toast from "../services/toast.js";
139
import { t } from "../services/i18n";
10+
import protected_session_holder from "../services/protected_session_holder";
11+
import toast from "../services/toast.js";
12+
import { dynamicRequire, isElectron, isMobile } from "../services/utils";
13+
import { ExtendedNoteType, TYPE_MAPPINGS, TypeWidget } from "./note_types";
14+
import { useNoteContext, useTriliumEvent } from "./react/hooks";
15+
import { TypeWidgetProps } from "./type_widgets/type_widget";
1416

1517
/**
1618
* The note detail is in charge of rendering the content of a note, by determining its type (e.g. text, code) and using the appropriate view widget.
@@ -80,7 +82,7 @@ export default function NoteDetail() {
8082
parentComponent.handleEvent("noteTypeMimeChanged", { noteId: note.noteId });
8183
} else if (note.noteId
8284
&& loadResults.isNoteReloaded(note.noteId, parentComponent.componentId)
83-
&& (type !== (await getWidgetType(note, noteContext)) || mime !== note?.mime)) {
85+
&& (type !== (await getExtendedWidgetType(note, noteContext)) || mime !== note?.mime)) {
8486
// this needs to have a triggerEvent so that e.g., note type (not in the component subtree) is updated
8587
parentComponent.triggerEvent("noteTypeMimeChanged", { noteId: note.noteId });
8688
} else {
@@ -212,7 +214,7 @@ export default function NoteDetail() {
212214
isVisible={type === itemType}
213215
isFullHeight={isFullHeight}
214216
props={props}
215-
/>
217+
/>;
216218
})}
217219
</div>
218220
);
@@ -254,7 +256,7 @@ function useNoteInfo() {
254256
const [ mime, setMime ] = useState<string>();
255257

256258
function refresh() {
257-
getWidgetType(actualNote, noteContext).then(type => {
259+
getExtendedWidgetType(actualNote, noteContext).then(type => {
258260
setNote(actualNote);
259261
setType(type);
260262
setMime(actualNote?.mime);
@@ -282,12 +284,12 @@ async function getCorrespondingWidget(type: ExtendedNoteType): Promise<null | Ty
282284
} else if (isValidElement(result)) {
283285
// Direct VNode provided.
284286
return result;
285-
} else {
286-
return result;
287287
}
288+
return result;
289+
288290
}
289291

290-
async function getWidgetType(note: FNote | null | undefined, noteContext: NoteContext | undefined): Promise<ExtendedNoteType | undefined> {
292+
export async function getExtendedWidgetType(note: FNote | null | undefined, noteContext: NoteContext | undefined): Promise<ExtendedNoteType | undefined> {
291293
if (!noteContext) return undefined;
292294
if (!note) {
293295
// If the note is null, then it's a new tab. If it's undefined, then it's not loaded yet.
@@ -324,7 +326,7 @@ async function getWidgetType(note: FNote | null | undefined, noteContext: NoteCo
324326
return resultingType;
325327
}
326328

327-
function checkFullHeight(noteContext: NoteContext | undefined, type: ExtendedNoteType | undefined) {
329+
export function checkFullHeight(noteContext: NoteContext | undefined, type: ExtendedNoteType | undefined) {
328330
if (!noteContext) return false;
329331

330332
// https://github.com/zadam/trilium/issues/2522

apps/client/src/widgets/layout/NoteTitleActions.tsx

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import "./NoteTitleActions.css";
22

33
import clsx from "clsx";
4+
import { useEffect, useState } from "preact/hooks";
45

6+
import NoteContext from "../../components/note_context";
57
import FNote from "../../entities/fnote";
68
import { t } from "../../services/i18n";
79
import CollectionProperties from "../note_bars/CollectionProperties";
10+
import { checkFullHeight, getExtendedWidgetType } from "../NoteDetail";
811
import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes";
9-
import Collapsible from "../react/Collapsible";
12+
import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible";
1013
import { useNoteContext, useNoteProperty } from "../react/hooks";
1114
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
1215

1316
export default function NoteTitleActions() {
14-
const { note, ntxId, componentId } = useNoteContext();
17+
const { note, ntxId, componentId, noteContext } = useNoteContext();
1518
const isHiddenNote = note && note.noteId !== "_search" && note.noteId.startsWith("_");
1619
const noteType = useNoteProperty(note, "type");
1720

1821
const items = [
19-
note && <PromotedAttributes note={note} componentId={componentId} />,
22+
note && <PromotedAttributes note={note} componentId={componentId} noteContext={noteContext} />,
2023
note && noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />,
2124
note && !isHiddenNote && noteType === "book" && <CollectionProperties note={note} />
2225
].filter(Boolean);
@@ -39,15 +42,29 @@ function SearchProperties({ note, ntxId }: { note: FNote, ntxId: string | null |
3942
);
4043
}
4144

42-
function PromotedAttributes({ note, componentId }: { note: FNote | null | undefined, componentId: string }) {
45+
function PromotedAttributes({ note, componentId, noteContext }: {
46+
note: FNote | null | undefined,
47+
componentId: string,
48+
noteContext: NoteContext | undefined
49+
}) {
4350
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
44-
if (!cells?.length) return false;
51+
const [ expanded, setExpanded ] = useState(false);
52+
53+
useEffect(() => {
54+
getExtendedWidgetType(note, noteContext).then(extendedNoteType => {
55+
const fullHeight = checkFullHeight(noteContext, extendedNoteType);
56+
setExpanded(!fullHeight);
57+
});
58+
}, [ note, noteContext ]);
4559

60+
if (!cells?.length) return false;
4661
return (note && (
47-
<Collapsible
62+
<ExternallyControlledCollapsible
63+
key={note.noteId}
4864
title={t("promoted_attributes.promoted_attributes")}
65+
expanded={expanded} setExpanded={setExpanded}
4966
>
5067
<PromotedAttributesContent note={note} componentId={componentId} cells={cells} setCells={setCells} />
51-
</Collapsible>
68+
</ExternallyControlledCollapsible>
5269
));
5370
}

apps/client/src/widgets/react/Collapsible.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@ interface CollapsibleProps extends Pick<HTMLAttributes<HTMLDivElement>, "classNa
1313
initiallyExpanded?: boolean;
1414
}
1515

16-
export default function Collapsible({ title, children, className, initiallyExpanded }: CollapsibleProps) {
16+
export default function Collapsible({ initiallyExpanded, ...restProps }: CollapsibleProps) {
17+
const [ expanded, setExpanded ] = useState(initiallyExpanded);
18+
return <ExternallyControlledCollapsible {...restProps} expanded={expanded} setExpanded={setExpanded} />;
19+
}
20+
21+
export function ExternallyControlledCollapsible({ title, children, className, expanded, setExpanded }: Omit<CollapsibleProps, "initiallyExpanded"> & {
22+
expanded: boolean | undefined;
23+
setExpanded: (expanded: boolean) => void
24+
}) {
1725
const bodyRef = useRef<HTMLDivElement>(null);
1826
const innerRef = useRef<HTMLDivElement>(null);
19-
const [ expanded, setExpanded ] = useState(initiallyExpanded);
2027
const { height } = useElementSize(innerRef) ?? {};
2128
const contentId = useUniqueName();
2229

@@ -48,5 +55,4 @@ export default function Collapsible({ title, children, className, initiallyExpan
4855
</div>
4956
</div>
5057
);
51-
5258
}

0 commit comments

Comments
 (0)