1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00
joplin/packages/lib/components/shared/side-menu-shared.ts
2024-03-02 14:25:27 +00:00

100 lines
3.5 KiB
TypeScript

import Folder from '../../models/Folder';
import BaseModel from '../../BaseModel';
import { FolderEntity, TagEntity } from '../../services/database/types';
import { getDisplayParentId, getTrashFolderId } from '../../services/trash';
interface Props {
folders: FolderEntity[];
selectedFolderId: string;
notesParentType: string;
collapsedFolderIds: string[];
selectedTagId: string;
tags?: TagEntity[];
}
export type RenderFolderItem = (folder: FolderEntity, selected: boolean, hasChildren: boolean, depth: number)=> any;
export type RenderTagItem = (tag: TagEntity, selected: boolean)=> any;
function folderHasChildren_(folders: FolderEntity[], folderId: string) {
if (folderId === getTrashFolderId()) {
return !!folders.find(f => !!f.deleted_time);
}
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];
const folderParentId = getDisplayParentId(folder, folders.find(f => f.id === folder.parent_id));
if (folderParentId === folderId) return true;
}
return false;
}
function folderIsCollapsed(folders: FolderEntity[], folderId: string, collapsedFolderIds: string[]) {
if (!collapsedFolderIds || !collapsedFolderIds.length) return false;
while (true) {
const folder: FolderEntity = BaseModel.byId(folders, folderId);
if (!folder) throw new Error(`No folder with id ${folder.id}`);
const folderParentId = getDisplayParentId(folder, folders.find(f => f.id === folder.parent_id));
if (!folderParentId) return false;
if (collapsedFolderIds.indexOf(folderParentId) >= 0) return true;
folderId = folderParentId;
}
}
function renderFoldersRecursive_(props: Props, renderItem: RenderFolderItem, items: any[], parentId: string, depth: number, order: string[]) {
const folders = props.folders;
for (let i = 0; i < folders.length; i++) {
const folder = folders[i];
const folderParentId = getDisplayParentId(folder, props.folders.find(f => f.id === folder.parent_id));
if (!Folder.idsEqual(folderParentId, parentId)) continue;
if (folderIsCollapsed(props.folders, folder.id, props.collapsedFolderIds)) continue;
const hasChildren = folderHasChildren_(folders, folder.id);
order.push(folder.id);
items.push(renderItem(folder, props.selectedFolderId === folder.id && props.notesParentType === 'Folder', hasChildren, depth));
if (hasChildren) {
const result = renderFoldersRecursive_(props, renderItem, items, folder.id, depth + 1, order);
items = result.items;
order = result.order;
}
}
return {
items: items,
order: order,
};
}
export const renderFolders = (props: Props, renderItem: RenderFolderItem) => {
return renderFoldersRecursive_(props, renderItem, [], '', 0, []);
};
export const renderTags = (props: Props, renderItem: RenderTagItem) => {
const tags = props.tags.slice();
tags.sort((a, b) => {
// It seems title can sometimes be undefined (perhaps when syncing
// and before tag has been decrypted?). It would be best to find
// the root cause but for now that will do.
//
// Fixes https://github.com/laurent22/joplin/issues/4051
if (!a || !a.title || !b || !b.title) return 0;
// Note: while newly created tags are normalized and lowercase
// imported tags might be any case, so we need to do case-insensitive
// sort.
return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : +1;
});
const tagItems = [];
const order: string[] = [];
for (let i = 0; i < tags.length; i++) {
const tag = tags[i];
order.push(tag.id);
tagItems.push(renderItem(tag, props.selectedTagId === tag.id && props.notesParentType === 'Tag'));
}
return {
items: tagItems,
order: order,
};
};