Skip to content

Commit 158f5ac

Browse files
committed
feat(layout): use collapsible for promoted attributes
1 parent fb70029 commit 158f5ac

File tree

3 files changed

+69
-43
lines changed

3 files changed

+69
-43
lines changed

apps/client/src/layouts/desktop_layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export default class DesktopLayout {
155155
.child(<ReadOnlyNoteInfoBar />)
156156
.child(<SharedInfo />)
157157
)
158-
.child(<PromotedAttributes />)
158+
.optChild(!isNewLayout, <PromotedAttributes />)
159159
.child(<SqlTableSchemas />)
160160
.child(<NoteDetail />)
161161
.child(<NoteList media="screen" />)

apps/client/src/widgets/PromotedAttributes.tsx

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
import { Dispatch, StateUpdater, useEffect, useRef, useState } from "preact/hooks";
21
import "./PromotedAttributes.css";
3-
import { useNoteContext, useNoteLabel, useTriliumEvent, useUniqueName } from "./react/hooks";
4-
import { Attribute } from "../services/attribute_parser";
5-
import FAttribute from "../entities/fattribute";
2+
3+
import { UpdateAttributeResponse } from "@triliumnext/commons";
64
import clsx from "clsx";
5+
import { ComponentChild, HTMLInputTypeAttribute, InputHTMLAttributes, MouseEventHandler, TargetedEvent, TargetedInputEvent } from "preact";
6+
import { Dispatch, StateUpdater, useEffect, useRef, useState } from "preact/hooks";
7+
8+
import FAttribute from "../entities/fattribute";
9+
import FNote from "../entities/fnote";
10+
import { Attribute } from "../services/attribute_parser";
11+
import attributes from "../services/attributes";
12+
import debounce from "../services/debounce";
713
import { t } from "../services/i18n";
814
import { DefinitionObject, extractAttributeDefinitionTypeAndName, LabelType } from "../services/promoted_attribute_definition_parser";
915
import server from "../services/server";
10-
import FNote from "../entities/fnote";
11-
import { ComponentChild, HTMLInputTypeAttribute, InputHTMLAttributes, MouseEventHandler, TargetedEvent, TargetedInputEvent } from "preact";
12-
import NoteAutocomplete from "./react/NoteAutocomplete";
1316
import ws from "../services/ws";
14-
import { UpdateAttributeResponse } from "@triliumnext/commons";
15-
import attributes from "../services/attributes";
16-
import debounce from "../services/debounce";
17+
import { useNoteContext, useNoteLabel, useTriliumEvent, useUniqueName } from "./react/hooks";
18+
import NoteAutocomplete from "./react/NoteAutocomplete";
1719

1820
interface Cell {
1921
uniqueId: string;
@@ -39,6 +41,15 @@ type OnChangeListener = (e: OnChangeEventData) => Promise<void>;
3941
export default function PromotedAttributes() {
4042
const { note, componentId } = useNoteContext();
4143
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
44+
return <PromotedAttributesContent note={note} componentId={componentId} cells={cells} setCells={setCells} />;
45+
}
46+
47+
export function PromotedAttributesContent({ note, componentId, cells, setCells }: {
48+
note: FNote | null | undefined;
49+
componentId: string;
50+
cells: Cell[] | undefined;
51+
setCells: Dispatch<StateUpdater<Cell[] | undefined>>;
52+
}) {
4253
const [ cellToFocus, setCellToFocus ] = useState<Cell>();
4354

4455
return (
@@ -62,7 +73,7 @@ export default function PromotedAttributes() {
6273
*
6374
* The cells are returned as a state since they can also be altered internally if needed, for example to add a new empty cell.
6475
*/
65-
function usePromotedAttributeData(note: FNote | null | undefined, componentId: string): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
76+
export function usePromotedAttributeData(note: FNote | null | undefined, componentId: string): [ Cell[] | undefined, Dispatch<StateUpdater<Cell[] | undefined>> ] {
6677
const [ viewType ] = useNoteLabel(note, "viewType");
6778
const [ cells, setCells ] = useState<Cell[]>();
6879

@@ -156,7 +167,7 @@ function PromotedAttributeCell(props: CellProps) {
156167
{correspondingInput}
157168
<MultiplicityCell {...props} />
158169
</div>
159-
)
170+
);
160171
}
161172

162173
const LABEL_MAPPINGS: Record<LabelType, HTMLInputTypeAttribute> = {
@@ -219,29 +230,29 @@ function LabelInput({ inputId, ...props }: CellProps & { inputId: string }) {
219230
<label className="tn-checkbox">{inputNode}</label>
220231
</div>
221232
<label for={inputId}>{definition.promotedAlias ?? valueName}</label>
222-
</>
223-
} else {
224-
return (
225-
<div className="input-group">
226-
{inputNode}
227-
{ definition.labelType === "color" && <ColorPicker {...props} onChange={onChangeListener} inputId={inputId} />}
228-
{ definition.labelType === "url" && (
229-
<InputButton
230-
className="open-external-link-button"
231-
icon="bx bx-window-open"
232-
title={t("promoted_attributes.open_external_link")}
233-
onClick={(e) => {
234-
const inputEl = document.getElementById(inputId) as HTMLInputElement | null;
235-
const url = inputEl?.value;
236-
if (url) {
237-
window.open(url, "_blank");
238-
}
239-
}}
240-
/>
241-
)}
242-
</div>
243-
);
233+
</>;
244234
}
235+
return (
236+
<div className="input-group">
237+
{inputNode}
238+
{ definition.labelType === "color" && <ColorPicker {...props} onChange={onChangeListener} inputId={inputId} />}
239+
{ definition.labelType === "url" && (
240+
<InputButton
241+
className="open-external-link-button"
242+
icon="bx bx-window-open"
243+
title={t("promoted_attributes.open_external_link")}
244+
onClick={(e) => {
245+
const inputEl = document.getElementById(inputId) as HTMLInputElement | null;
246+
const url = inputEl?.value;
247+
if (url) {
248+
window.open(url, "_blank");
249+
}
250+
}}
251+
/>
252+
)}
253+
</div>
254+
);
255+
245256
}
246257

247258

@@ -282,7 +293,7 @@ function ColorPicker({ cell, onChange, inputId }: CellProps & {
282293
}}
283294
/>
284295
</>
285-
)
296+
);
286297
}
287298

288299
function RelationInput({ inputId, ...props }: CellProps & { inputId: string }) {
@@ -295,7 +306,7 @@ function RelationInput({ inputId, ...props }: CellProps & { inputId: string }) {
295306
await updateAttribute(note, cell, componentId, value, setCells);
296307
}}
297308
/>
298-
)
309+
);
299310
}
300311

301312
function MultiplicityCell({ cell, cells, setCells, setCellToFocus, note, componentId }: CellProps) {
@@ -346,13 +357,13 @@ function MultiplicityCell({ cell, cells, setCells, setCellToFocus, note, compone
346357
name: cell.valueName,
347358
value: ""
348359
}
349-
})
360+
});
350361
}
351362
setCells(cells.toSpliced(index, 1, ...newOnesToInsert));
352363
}}
353364
/>
354365
</td>
355-
)
366+
);
356367
}
357368

358369
function PromotedActionButton({ icon, title, onClick }: {
@@ -366,7 +377,7 @@ function PromotedActionButton({ icon, title, onClick }: {
366377
title={title}
367378
onClick={onClick}
368379
/>
369-
)
380+
);
370381
}
371382

372383
function InputButton({ icon, className, title, onClick }: {
@@ -381,7 +392,7 @@ function InputButton({ icon, className, title, onClick }: {
381392
title={title}
382393
onClick={onClick}
383394
/>
384-
)
395+
);
385396
}
386397

387398
function setupTextLabelAutocomplete(el: HTMLInputElement, valueAttr: Attribute, onChangeListener: OnChangeListener) {
@@ -406,7 +417,7 @@ function setupTextLabelAutocomplete(el: HTMLInputElement, valueAttr: Attribute,
406417
[
407418
{
408419
displayKey: "value",
409-
source: function (term, cb) {
420+
source (term, cb) {
410421
term = term.toLowerCase();
411422

412423
const filtered = attributeValues.filter((attr) => attr.value.toLowerCase().includes(term));

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@ import clsx from "clsx";
55
import FNote from "../../entities/fnote";
66
import { t } from "../../services/i18n";
77
import CollectionProperties from "../note_bars/CollectionProperties";
8+
import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes";
89
import Collapsible from "../react/Collapsible";
910
import { useNoteContext, useNoteProperty } from "../react/hooks";
1011
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
1112

1213
export default function NoteTitleActions() {
13-
const { note, ntxId } = useNoteContext();
14+
const { note, ntxId, componentId } = useNoteContext();
1415
const isHiddenNote = note && note.noteId !== "_search" && note.noteId.startsWith("_");
1516
const noteType = useNoteProperty(note, "type");
1617

1718
const items = [
19+
note && <PromotedAttributes note={note} componentId={componentId} />,
1820
note && noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />,
1921
note && !isHiddenNote && noteType === "book" && <CollectionProperties note={note} />
2022
].filter(Boolean);
@@ -36,3 +38,16 @@ function SearchProperties({ note, ntxId }: { note: FNote, ntxId: string | null |
3638
</Collapsible>
3739
);
3840
}
41+
42+
function PromotedAttributes({ note, componentId }: { note: FNote | null | undefined, componentId: string }) {
43+
const [ cells, setCells ] = usePromotedAttributeData(note, componentId);
44+
if (!cells?.length) return false;
45+
46+
return (note && (
47+
<Collapsible
48+
title={t("promoted_attributes.promoted_attributes")}
49+
>
50+
<PromotedAttributesContent note={note} componentId={componentId} cells={cells} setCells={setCells} />
51+
</Collapsible>
52+
));
53+
}

0 commit comments

Comments
 (0)