refactor(react): add type safety for note labels

This commit is contained in:
Elian Doran
2025-09-14 10:16:52 +03:00
parent 8a66ee7565
commit b8e4947adb
11 changed files with 84 additions and 36 deletions

View File

@@ -23,7 +23,7 @@ import { ContentLanguagesList } from "../type_widgets/options/i18n";
export default function BasicPropertiesTab({ note }: TabContext) {
return (
<div className="basic-properties-widget">
<div className="basic-properties-widget">
<NoteTypeWidget note={note} />
<ProtectedNoteSwitch note={note} />
<EditabilitySelect note={note} />
@@ -43,7 +43,7 @@ function NoteTypeWidget({ note }: { note?: FNote | null }) {
return mime_types.getMimeTypes().filter(mimeType => mimeType.enabled)
}, [ codeNotesMimeTypes ]);
const notSelectableNoteTypes = useMemo(() => NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type), []);
const currentNoteType = useNoteProperty(note, "type") ?? undefined;
const currentNoteMime = useNoteProperty(note, "mime");
const [ modalShown, setModalShown ] = useState(false);
@@ -95,7 +95,7 @@ function NoteTypeWidget({ note }: { note?: FNote | null }) {
checked={checked}
badges={badges}
onClick={() => changeNoteType(type, mime)}
>{title}</FormListItem>
>{title}</FormListItem>
);
} else {
return (
@@ -103,7 +103,7 @@ function NoteTypeWidget({ note }: { note?: FNote | null }) {
<FormDropdownDivider />
<FormListItem
checked={checked}
disabled
disabled
>
<strong>{title}</strong>
</FormListItem>
@@ -131,7 +131,7 @@ function NoteTypeWidget({ note }: { note?: FNote | null }) {
<CodeMimeTypesList />
</Modal>
</div>
)
)
}
function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
@@ -151,7 +151,7 @@ function ProtectedNoteSwitch({ note }: { note?: FNote | null }) {
function EditabilitySelect({ note }: { note?: FNote | null }) {
const [ readOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
const [ autoReadOnlyDisabled, setAutoReadOnlyDisabled ] = useNoteLabelBoolean(note, "autoReadOnlyDisabled");
const [ autoReadOnlyDisabled, setAutoReadOnlyDisabled ] = useNoteLabelBoolean(note, "autoReadOnlyDisabled");
const options = useMemo(() => ([
{
@@ -208,7 +208,7 @@ function BookmarkSwitch({ note }: { note?: FNote | null }) {
<FormToggle
switchOnName={t("bookmark_switch.bookmark")} switchOnTooltip={t("bookmark_switch.bookmark_this_note")}
switchOffName={t("bookmark_switch.bookmark")} switchOffTooltip={t("bookmark_switch.remove_bookmark")}
currentValue={isBookmarked}
currentValue={isBookmarked}
onChange={async (shouldBookmark) => {
if (!note) return;
const resp = await server.put<ToggleInParentResponse>(`notes/${note.noteId}/toggle-in-parent/_lbBookmarks/${shouldBookmark}`);
@@ -260,11 +260,11 @@ function SharedSwitch({ note }: { note?: FNote | null }) {
} else {
if (note?.getParentBranches().length === 1 && !(await dialog.confirm(t("shared_switch.shared-branch")))) {
return;
}
}
const shareBranch = note?.getParentBranches().find((b) => b.parentNoteId === "_share");
if (!shareBranch?.branchId) return;
await server.remove(`branches/${shareBranch.branchId}?taskId=no-progress-reporting`);
await server.remove(`branches/${shareBranch.branchId}?taskId=no-progress-reporting`);
}
sync.syncNow(true);
@@ -330,7 +330,7 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
return locales.find(locale => typeof locale === "object" && locale.id === currentNoteLanguage) as Locale | undefined;
}, [ currentNoteLanguage ]);
return (
return (
<div className="note-language-container">
<span>{t("basic_properties.language")}:</span>
&nbsp;
@@ -350,7 +350,7 @@ function NoteLanguageSwitch({ note }: { note?: FNote | null }) {
<FormListItem
onClick={() => setModalShown(true)}
>{t("note_language.configure-languages")}</FormListItem>
>{t("note_language.configure-languages")}</FormListItem>
</Dropdown>
<HelpButton helpPage="B0lcI9xz1r8K" style={{ marginLeft: "4px" }} />
@@ -378,4 +378,4 @@ function findTypeTitle(type?: NoteType, mime?: string | null) {
return noteType ? noteType.title : type;
}
}
}

View File

@@ -118,6 +118,7 @@ function CheckboxPropertyView({ note, property }: { note: FNote, property: Check
}
function NumberPropertyView({ note, property }: { note: FNote, property: NumberProperty }) {
//@ts-expect-error Interop with text box which takes in string values even for numbers.
const [ value, setValue ] = useNoteLabel(note, property.bindToLabel);
return (

View File

@@ -17,4 +17,4 @@ export default function NotePropertiesTab({ note }: TabContext) {
)}
</div>
)
}
}

View File

@@ -25,4 +25,4 @@ export default function ScriptTab({ note }: TabContext) {
</div>
</div>
);
}
}

View File

@@ -57,7 +57,7 @@ export const SEARCH_OPTIONS: SearchOption[] = [
defaultValue: "root",
icon: "bx bx-filter-alt",
label: t("search_definition.ancestor"),
component: AncestorOption,
component: AncestorOption,
additionalAttributesToDelete: [ { type: "label", name: "ancestorDepth" } ]
},
{
@@ -173,7 +173,7 @@ function SearchStringOption({ note, refreshResults, error, ...restProps }: Searc
}
}, [ error ]);
return <SearchOption
return <SearchOption
title={t("search_string.title_column")}
help={<>
<strong>{t("search_string.search_syntax")}</strong> - {t("search_string.also_see")} <a href="#" data-help-page="search.html">{t("search_string.complete_help")}</a>
@@ -243,7 +243,7 @@ function AncestorOption({ note, ...restProps}: SearchOptionProps) {
const options: { value: string | undefined; label: string }[] = [
{ value: "", label: t("ancestor.depth_doesnt_matter") },
{ value: "eq1", label: `${t("ancestor.depth_eq", { count: 1 })} (${t("ancestor.direct_children")})` }
];
];
for (let i=2; i<=9; i++) options.push({ value: "eq" + i, label: t("ancestor.depth_eq", { count: i }) });
for (let i=0; i<=9; i++) options.push({ value: "gt" + i, label: t("ancestor.depth_gt", { count: i }) });
@@ -253,7 +253,7 @@ function AncestorOption({ note, ...restProps}: SearchOptionProps) {
}, []);
return <SearchOption
title={t("ancestor.label")}
title={t("ancestor.label")}
note={note} {...restProps}
>
<div style={{display: "flex", alignItems: "center"}}>
@@ -357,4 +357,4 @@ function LimitOption({ note, defaultValue, ...restProps }: SearchOptionProps) {
currentValue={limit ?? defaultValue} onChange={setLimit}
/>
</SearchOption>
}
}

View File

@@ -4,6 +4,7 @@ import attributes from "../../services/attributes";
import NoteContextAwareWidget from "../note_context_aware_widget";
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, type MapLayer } from "../collections/geomap/map_layer";
import { ViewTypeOptions } from "../collections/interface";
import { FilterLabelsByType } from "@triliumnext/commons";
interface BookConfig {
properties: BookProperty[];
@@ -12,7 +13,7 @@ interface BookConfig {
export interface CheckBoxProperty {
type: "checkbox",
label: string;
bindToLabel: string
bindToLabel: FilterLabelsByType<boolean>
}
export interface ButtonProperty {
@@ -26,7 +27,7 @@ export interface ButtonProperty {
export interface NumberProperty {
type: "number",
label: string;
bindToLabel: string;
bindToLabel: FilterLabelsByType<number>;
width?: number;
min?: number;
}
@@ -44,7 +45,7 @@ interface ComboBoxGroup {
export interface ComboBoxProperty {
type: "combobox",
label: string;
bindToLabel: string;
bindToLabel: FilterLabelsByType<string>;
/**
* The default value is used when the label is not set.
*/