feat(react): port sort child notes

This commit is contained in:
Elian Doran 2025-08-03 21:18:18 +03:00
parent 164feaa3ec
commit bca397e3e4
No known key found for this signature in database
13 changed files with 190 additions and 121 deletions

View File

@ -305,7 +305,7 @@
"sort_with_respect_to_different_character_sorting": "根据不同语言或地区的字符排序和排序规则排序。", "sort_with_respect_to_different_character_sorting": "根据不同语言或地区的字符排序和排序规则排序。",
"natural_sort_language": "自然排序语言", "natural_sort_language": "自然排序语言",
"the_language_code_for_natural_sort": "自然排序的语言代码,例如中文的 \"zh-CN\"。", "the_language_code_for_natural_sort": "自然排序的语言代码,例如中文的 \"zh-CN\"。",
"sort": "排序 <kbd>Enter</kbd>" "sort": "排序"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "上传附件到笔记", "upload_attachments_to_note": "上传附件到笔记",

View File

@ -303,7 +303,7 @@
"sort_with_respect_to_different_character_sorting": "Sortierung im Hinblick auf unterschiedliche Sortier- und Sortierregeln für Zeichen in verschiedenen Sprachen oder Regionen.", "sort_with_respect_to_different_character_sorting": "Sortierung im Hinblick auf unterschiedliche Sortier- und Sortierregeln für Zeichen in verschiedenen Sprachen oder Regionen.",
"natural_sort_language": "Natürliche Sortiersprache", "natural_sort_language": "Natürliche Sortiersprache",
"the_language_code_for_natural_sort": "Der Sprachcode für die natürliche Sortierung, z. B. \"de-DE\" für Deutsch.", "the_language_code_for_natural_sort": "Der Sprachcode für die natürliche Sortierung, z. B. \"de-DE\" für Deutsch.",
"sort": "Sortieren <kbd>Eingabetaste</kbd>" "sort": "Sortieren"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "Lade Anhänge zur Notiz hoch", "upload_attachments_to_note": "Lade Anhänge zur Notiz hoch",

View File

@ -309,7 +309,7 @@
"sort_with_respect_to_different_character_sorting": "sort with respect to different character sorting and collation rules in different languages or regions.", "sort_with_respect_to_different_character_sorting": "sort with respect to different character sorting and collation rules in different languages or regions.",
"natural_sort_language": "Natural sort language", "natural_sort_language": "Natural sort language",
"the_language_code_for_natural_sort": "The language code for natural sort, e.g. \"zh-CN\" for Chinese.", "the_language_code_for_natural_sort": "The language code for natural sort, e.g. \"zh-CN\" for Chinese.",
"sort": "Sort <kbd>enter</kbd>" "sort": "Sort"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "Upload attachments to note", "upload_attachments_to_note": "Upload attachments to note",

View File

@ -308,7 +308,7 @@
"sort_with_respect_to_different_character_sorting": "ordenar con respecto a diferentes reglas de ordenamiento y clasificación de caracteres en diferentes idiomas o regiones.", "sort_with_respect_to_different_character_sorting": "ordenar con respecto a diferentes reglas de ordenamiento y clasificación de caracteres en diferentes idiomas o regiones.",
"natural_sort_language": "Idioma de clasificación natural", "natural_sort_language": "Idioma de clasificación natural",
"the_language_code_for_natural_sort": "El código del idioma para el ordenamiento natural, ej. \"zh-CN\" para Chino.", "the_language_code_for_natural_sort": "El código del idioma para el ordenamiento natural, ej. \"zh-CN\" para Chino.",
"sort": "Ordenar <kbd>Enter</kbd>" "sort": "Ordenar"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "Cargar archivos adjuntos a nota", "upload_attachments_to_note": "Cargar archivos adjuntos a nota",

View File

@ -303,7 +303,7 @@
"sort_with_respect_to_different_character_sorting": "trier en fonction de différentes règles de tri et de classement des caractères dans différentes langues ou régions.", "sort_with_respect_to_different_character_sorting": "trier en fonction de différentes règles de tri et de classement des caractères dans différentes langues ou régions.",
"natural_sort_language": "Langage de tri naturel", "natural_sort_language": "Langage de tri naturel",
"the_language_code_for_natural_sort": "Le code de langue pour le tri naturel, par ex. \"zh-CN\" pour le chinois.", "the_language_code_for_natural_sort": "Le code de langue pour le tri naturel, par ex. \"zh-CN\" pour le chinois.",
"sort": "Trier <kbd>Entrée</kbd>" "sort": "Trier"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "Téléverser des pièces jointes à la note", "upload_attachments_to_note": "Téléverser des pièces jointes à la note",

View File

@ -1193,7 +1193,7 @@
"folders": "Dosare", "folders": "Dosare",
"natural_sort": "Ordonare naturală", "natural_sort": "Ordonare naturală",
"natural_sort_language": "Limba pentru ordonare naturală", "natural_sort_language": "Limba pentru ordonare naturală",
"sort": "Ordonare <kbd>Enter</kbd>", "sort": "Ordonare",
"sort_children_by": "Ordonează subnotițele după...", "sort_children_by": "Ordonează subnotițele după...",
"sort_folders_at_top": "ordonează dosarele primele", "sort_folders_at_top": "ordonează dosarele primele",
"sort_with_respect_to_different_character_sorting": "ordonează respectând regulile de sortare și clasificare diferite în funcție de limbă și regiune.", "sort_with_respect_to_different_character_sorting": "ordonează respectând regulile de sortare și clasificare diferite în funcție de limbă și regiune.",

View File

@ -309,7 +309,7 @@
"sort_with_respect_to_different_character_sorting": "sortiranje sa poštovanjem različitih pravila sortiranja karaktera i kolacija u različitim jezicima ili regionima.", "sort_with_respect_to_different_character_sorting": "sortiranje sa poštovanjem različitih pravila sortiranja karaktera i kolacija u različitim jezicima ili regionima.",
"natural_sort_language": "Jezik za prirodno sortiranje", "natural_sort_language": "Jezik za prirodno sortiranje",
"the_language_code_for_natural_sort": "Kod jezika za prirodno sortiranje, npr. \"zh-CN\" za Kineski.", "the_language_code_for_natural_sort": "Kod jezika za prirodno sortiranje, npr. \"zh-CN\" za Kineski.",
"sort": "Sortiraj <kbd>enter</kbd>" "sort": "Sortiraj"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "Otpremite priloge uz belešku", "upload_attachments_to_note": "Otpremite priloge uz belešku",

View File

@ -278,7 +278,7 @@
"sort_with_respect_to_different_character_sorting": "根據不同語言或地區的字符排序和排序規則排序。", "sort_with_respect_to_different_character_sorting": "根據不同語言或地區的字符排序和排序規則排序。",
"natural_sort_language": "自然排序語言", "natural_sort_language": "自然排序語言",
"the_language_code_for_natural_sort": "自然排序的語言程式碼,例如繁體中文的 \"zh-TW\"。", "the_language_code_for_natural_sort": "自然排序的語言程式碼,例如繁體中文的 \"zh-TW\"。",
"sort": "排序 <kbd>Enter</kbd>" "sort": "排序"
}, },
"upload_attachments": { "upload_attachments": {
"upload_attachments_to_note": "上傳附件到筆記", "upload_attachments_to_note": "上傳附件到筆記",

View File

@ -1,113 +0,0 @@
import type { EventData } from "../../components/app_context.js";
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
import { t } from "../../services/i18n.js";
import server from "../../services/server.js";
import BasicWidget from "../basic_widget.js";
const TPL = /*html*/`<div class="sort-child-notes-dialog modal mx-auto" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" style="max-width: 500px" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${t("sort_child_notes.sort_children_by")}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="${t("sort_child_notes.close")}"></button>
</div>
<form class="sort-child-notes-form">
<div class="modal-body">
<h5>${t("sort_child_notes.sorting_criteria")}</h5>
<div class="form-check">
<label for="sort-by-title" class="form-check-label tn-radio">
<input id="sort-by-title" class="form-check-input" type="radio" name="sort-by" value="title" checked>
${t("sort_child_notes.title")}
</label>
</div>
<div class="form-check">
<label for="sort-by-dateCreated" class="form-check-label tn-radio">
<input id="sort-by-dateCreated" class="form-check-input" type="radio" name="sort-by" value="dateCreated">
${t("sort_child_notes.date_created")}
</label>
</div>
<div class="form-check">
<label for="sort-by-dateModified" class="form-check-label tn-radio">
<input id="sort-by-dateModified" class="form-check-input" type="radio" name="sort-by" value="dateModified">
${t("sort_child_notes.date_modified")}
</label>
</div>
<br/>
<h5>${t("sort_child_notes.sorting_direction")}</h5>
<div class="form-check">
<label for="sort-direction-asc" class="form-check-label tn-radio">
<input id="sort-direction-asc" class="form-check-input" type="radio" name="sort-direction" value="asc" checked>
${t("sort_child_notes.ascending")}
</label>
</div>
<div class="form-check">
<label for="sort-direction-desc" class="form-check-label tn-radio">
<input id="sort-direction-desc" class="form-check-input" type="radio" name="sort-direction" value="desc">
${t("sort_child_notes.descending")}
</label>
</div>
<br />
<h5>${t("sort_child_notes.folders")}</h5>
<div class="form-check">
<label for="sort-folders-first" class="form-check-label tn-checkbox">
<input id="sort-folders-first" class="form-check-input" type="checkbox" name="sort-folders-first" value="1">
${t("sort_child_notes.sort_folders_at_top")}
</label>
</div>
<br />
<h5>${t("sort_child_notes.natural_sort")}</h5>
<div class="form-check">
<label for="sort-natural" class="form-check-label tn-checkbox">
<input id="sort-natural" class="form-check-input" type="checkbox" name="sort-natural" value="1">
${t("sort_child_notes.sort_with_respect_to_different_character_sorting")}
</label>
</div>
<br />
<div class="form-check">
<label>
${t("sort_child_notes.natural_sort_language")}
<input class="form-control" name="sort-locale">
${t("sort_child_notes.the_language_code_for_natural_sort")}
</label>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">${t("sort_child_notes.sort")}</button>
</div>
</form>
</div>
</div>
</div>`;
export default class SortChildNotesDialog extends BasicWidget {
private parentNoteId?: string;
private $form!: JQuery<HTMLElement>;
doRender() {
this.$widget = $(TPL);
this.$form = this.$widget.find(".sort-child-notes-form");
this.$form.on("submit", async (e) => {
e.preventDefault();
const sortBy = this.$form.find("input[name='sort-by']:checked").val();
const sortDirection = this.$form.find("input[name='sort-direction']:checked").val();
const foldersFirst = this.$form.find("input[name='sort-folders-first']").is(":checked");
const sortNatural = this.$form.find("input[name='sort-natural']").is(":checked");
const sortLocale = this.$form.find("input[name='sort-locale']").val();
await server.put(`notes/${this.parentNoteId}/sort-children`, { sortBy, sortDirection, foldersFirst, sortNatural, sortLocale });
closeActiveDialog();
});
}
async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
this.parentNoteId = node.data.noteId;
openDialog(this.$widget);
this.$form.find("input:first").focus();
}
}

View File

@ -0,0 +1,104 @@
import { useState } from "preact/hooks";
import { EventData } from "../../components/app_context";
import { closeActiveDialog, openDialog } from "../../services/dialog";
import { t } from "../../services/i18n";
import Button from "../react/Button";
import FormCheckbox from "../react/FormCheckbox";
import FormRadioGroup from "../react/FormRadioGroup";
import FormTextBox from "../react/FormTextBox";
import Modal from "../react/Modal";
import ReactBasicWidget from "../react/ReactBasicWidget";
import server from "../../services/server";
function SortChildNotesDialogComponent({ parentNoteId }: { parentNoteId?: string }) {
const [ sortBy, setSortBy ] = useState("title");
const [ sortDirection, setSortDirection ] = useState("asc");
const [ foldersFirst, setFoldersFirst ] = useState(false);
const [ sortNatural, setSortNatural ] = useState(false);
const [ sortLocale, setSortLocale ] = useState("");
async function onSubmit() {
await server.put(`notes/${parentNoteId}/sort-children`, {
sortBy,
sortDirection,
foldersFirst,
sortNatural,
sortLocale
});
// Close the dialog after submission
closeActiveDialog();
}
return (parentNoteId &&
<Modal
className="sort-child-notes-dialog"
title={t("sort_child_notes.sort_children_by")}
size="lg"
onSubmit={onSubmit}
footer={<Button text={t("sort_child_notes.sort")} keyboardShortcut="Enter" />}
>
<h5>{t("sort_child_notes.sorting_criteria")}</h5>
<FormRadioGroup
name="sort-by"
values={[
{ value: "title", label: t("sort_child_notes.title") },
{ value: "dateCreated", label: t("sort_child_notes.date_created") },
{ value: "dateModified", label: t("sort_child_notes.date_modified") }
]}
currentValue={sortBy} onChange={setSortBy}
/>
<br/>
<h5>{t("sort_child_notes.sorting_direction")}</h5>
<FormRadioGroup
name="sort-direction"
values={[
{ value: "asc", label: t("sort_child_notes.ascending") },
{ value: "desc", label: t("sort_child_notes.descending") }
]}
currentValue={sortDirection} onChange={setSortDirection}
/>
<br/>
<h5>{t("sort_child_notes.folders")}</h5>
<FormCheckbox
label={t("sort_child_notes.sort_folders_at_top")}
name="sort-folders-first"
currentValue={foldersFirst} onChange={setFoldersFirst}
/>
<br />
<h5>{t("sort_child_notes.natural_sort")}</h5>
<FormCheckbox
name="sort-natural"
label={t("sort_child_notes.sort_with_respect_to_different_character_sorting")}
currentValue={sortNatural} onChange={setSortNatural}
/>
<FormTextBox
className="form-check"
name="sort-locale"
label={t("sort_child_notes.natural_sort_language")}
description={t("sort_child_notes.the_language_code_for_natural_sort")}
currentValue={sortLocale} onChange={setSortLocale}
/>
</Modal>
)
}
export default class SortChildNotesDialog extends ReactBasicWidget {
private parentNoteId?: string;
get component() {
return <SortChildNotesDialogComponent parentNoteId={this.parentNoteId} />;
}
async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
this.parentNoteId = node.data.noteId;
this.doRender();
openDialog(this.$widget);
}
}

View File

@ -0,0 +1,23 @@
interface FormCheckboxProps {
name: string;
label: string;
currentValue?: boolean;
onChange(newValue: boolean): void;
}
export default function FormCheckbox({ name, label, currentValue, onChange }: FormCheckboxProps) {
return (
<div className="form-check">
<label className="form-check-label tn-checkbox">
<input
className="form-check-input"
type="checkbox"
name={name}
checked={currentValue || false}
value="1"
onChange={e => onChange((e.target as HTMLInputElement).checked)} />
{label}
</label>
</div>
);
}

View File

@ -0,0 +1,30 @@
interface FormRadioProps {
name: string;
currentValue?: string;
values: {
value: string;
label: string;
}[];
onChange(newValue: string): void;
}
export default function FormRadioGroup({ name, values, currentValue, onChange }: FormRadioProps) {
return (
<>
{(values || []).map(({ value, label }) => (
<div className="form-check">
<label className="form-check-label tn-radio">
<input
className="form-check-input"
type="radio"
name={name}
value={value}
checked={value === currentValue}
onChange={e => onChange((e.target as HTMLInputElement).value)} />
{label}
</label>
</div>
))}
</>
);
}

View File

@ -0,0 +1,25 @@
interface FormTextBoxProps {
name: string;
label: string;
currentValue?: string;
className?: string;
description?: string;
onChange?(newValue: string): void;
}
export default function FormTextBox({ name, label, description, className, currentValue, onChange }: FormTextBoxProps) {
return (
<div className={className}>
<label>
{label}
<input
type="text"
className="form-control"
name={name}
value={currentValue}
onInput={e => onChange?.(e.currentTarget.value)} />
{description && <small className="form-text text-muted">{description}</small>}
</label>
</div>
);
}