feat(board/relation): basic support for editing relations in columns

This commit is contained in:
Elian Doran 2025-11-15 11:11:42 +02:00
parent d1e80815d5
commit 092a84693f
No known key found for this signature in database
4 changed files with 69 additions and 39 deletions

View File

@ -119,7 +119,7 @@ export default function Card({
setTitle(newTitle);
}}
dismiss={() => api.dismissEditingTitle()}
multiline
mode="multiline"
/>
)}
</div>

View File

@ -124,6 +124,7 @@ export default function Column({
currentValue={column}
save={newTitle => api.renameColumn(column, newTitle)}
dismiss={() => setColumnNameToEdit?.(undefined)}
mode={isInRelationMode ? "relation" : "normal"}
/>
)}
</h3>
@ -187,7 +188,7 @@ function AddNewItem({ column, api }: { column: string, api: BoardApi }) {
placeholder={t("board_view.new-item-placeholder")}
save={(title) => api.createNewItem(column, title)}
dismiss={() => setIsCreatingNewItem(false)}
multiline isNewItem
mode="multiline" isNewItem
/>
)}
</div>

View File

@ -13,6 +13,7 @@ import Column from "./column";
import BoardApi from "./api";
import FormTextArea from "../../react/FormTextArea";
import FNote from "../../../entities/fnote";
import NoteAutocomplete from "../../react/NoteAutocomplete";
export interface BoardViewData {
columns?: BoardColumnData[];
@ -188,14 +189,14 @@ export default function BoardView({ note: parentNote, noteIds, viewConfig, saveC
<div className="column-drop-placeholder show" />
)}
<AddNewColumn api={api} />
<AddNewColumn api={api} isInRelationMode={isInRelationMode} />
</div>
</BoardViewContext.Provider>
</div>
)
}
function AddNewColumn({ api }: { api: BoardApi }) {
function AddNewColumn({ api, isInRelationMode }: { api: BoardApi, isInRelationMode: boolean }) {
const [ isCreatingNewColumn, setIsCreatingNewColumn ] = useState(false);
const addColumnCallback = useCallback(() => {
@ -215,19 +216,20 @@ function AddNewColumn({ api }: { api: BoardApi }) {
save={(columnName) => api.addNewColumn(columnName)}
dismiss={() => setIsCreatingNewColumn(false)}
isNewItem
mode={isInRelationMode ? "relation" : "normal"}
/>
)}
</div>
)
}
export function TitleEditor({ currentValue, placeholder, save, dismiss, multiline, isNewItem }: {
export function TitleEditor({ currentValue, placeholder, save, dismiss, mode, isNewItem }: {
currentValue?: string;
placeholder?: string;
save: (newValue: string) => void;
dismiss: () => void;
multiline?: boolean;
isNewItem?: boolean;
mode?: "normal" | "multiline" | "relation";
}) {
const inputRef = useRef<any>(null);
const focusElRef = useRef<Element>(null);
@ -240,8 +242,6 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, multilin
inputRef.current?.select();
}, [ inputRef ]);
const Element = multiline ? FormTextArea : FormTextBox;
useEffect(() => {
if (dismissOnNextRefreshRef.current) {
dismiss();
@ -249,14 +249,7 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, multilin
}
});
return (
<Element
inputRef={inputRef}
currentValue={currentValue ?? ""}
placeholder={placeholder}
autoComplete="trilium-title-entry" // forces the auto-fill off better than the "off" value.
rows={multiline ? 4 : undefined}
onKeyDown={(e: TargetedKeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const onKeyDown = (e: TargetedKeyboardEvent<HTMLInputElement | HTMLTextAreaElement> | KeyboardEvent) => {
if (e.key === "Enter" || e.key === "Escape") {
e.preventDefault();
e.stopPropagation();
@ -265,15 +258,43 @@ export function TitleEditor({ currentValue, placeholder, save, dismiss, multilin
focusElRef.current.focus();
}
}
}}
onBlur={(newValue) => {
};
const onBlur = (newValue) => {
if (!shouldDismiss.current && newValue.trim() && (newValue !== currentValue || isNewItem)) {
save(newValue);
dismissOnNextRefreshRef.current = true;
} else {
dismiss();
}
}}
};
if (mode !== "relation") {
const Element = mode === "multiline" ? FormTextArea : FormTextBox;
return (
<Element
inputRef={inputRef}
currentValue={currentValue ?? ""}
placeholder={placeholder}
autoComplete="trilium-title-entry" // forces the auto-fill off better than the "off" value.
rows={mode === "multiline" ? 4 : undefined}
onKeyDown={onKeyDown}
onBlur={onBlur}
/>
);
} else {
return (
<NoteAutocomplete
inputRef={inputRef}
noteId={currentValue ?? ""}
opts={{
hideAllButtons: true,
allowCreatingNotes: true
}}
onKeyDown={onKeyDown}
onBlur={onBlur}
/>
)
}
}

View File

@ -15,11 +15,13 @@ interface NoteAutocompleteProps {
opts?: Omit<Options, "container">;
onChange?: (suggestion: Suggestion | null) => void;
onTextChange?: (text: string) => void;
onKeyDown?: (e: KeyboardEvent) => void;
onBlur?: (newValue: string) => void;
noteIdChanged?: (noteId: string) => void;
noteId?: string;
}
export default function NoteAutocomplete({ id, inputRef: externalInputRef, text, placeholder, onChange, onTextChange, container, containerStyle, opts, noteId, noteIdChanged }: NoteAutocompleteProps) {
export default function NoteAutocomplete({ id, inputRef: externalInputRef, text, placeholder, onChange, onTextChange, container, containerStyle, opts, noteId, noteIdChanged, onKeyDown, onBlur }: NoteAutocompleteProps) {
const ref = useSyncedRef<HTMLInputElement>(externalInputRef);
useEffect(() => {
@ -57,6 +59,12 @@ export default function NoteAutocomplete({ id, inputRef: externalInputRef, text,
if (onTextChange) {
$autoComplete.on("input", () => onTextChange($autoComplete[0].value));
}
if (onKeyDown) {
$autoComplete.on("keydown", (e) => e.originalEvent && onKeyDown(e.originalEvent));
}
if (onBlur) {
$autoComplete.on("blur", () => onBlur($autoComplete.getSelectedNoteId() ?? ""));
}
}, [opts, container?.current]);
useEffect(() => {