1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-20 23:30:05 +02:00
Files
joplin/packages/app-desktop/gui/NoteList/NoteList2.tsx

162 lines
5.0 KiB
TypeScript
Raw Normal View History

2023-08-06 17:21:09 +01:00
import * as React from 'react';
2023-08-12 15:45:19 +01:00
import { _ } from '@joplin/lib/locale';
import { useMemo, useCallback, useState } from 'react';
2023-08-06 17:21:09 +01:00
import { AppState } from '../../app.reducer';
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
const { connect } = require('react-redux');
2023-08-11 18:18:32 +01:00
import { ItemFlow, Props } from './utils/types';
2023-08-06 17:21:09 +01:00
import { itemIsReadOnlySync, ItemSlice } from '@joplin/lib/models/utils/readOnly';
2023-08-11 18:18:32 +01:00
import { FolderEntity } from '@joplin/lib/services/database/types';
2023-08-06 17:21:09 +01:00
import ItemChange from '@joplin/lib/models/ItemChange';
import { Size } from '@joplin/utils/types';
2023-08-11 18:18:32 +01:00
import defaultListRenderer from './utils/defaultListRenderer';
2023-08-11 18:52:10 +01:00
import NoteListItem from '../NoteListItem/NoteListItem';
2023-08-11 18:18:32 +01:00
import useRenderedNotes from './utils/useRenderedNote';
2023-08-11 18:34:12 +01:00
import useItemCss from './utils/useItemCss';
2023-08-11 19:19:17 +01:00
import useOnContextMenu from '../NoteListItem/utils/useOnContextMenu';
2023-08-12 15:45:19 +01:00
import useVisibleRange from './utils/useVisibleRange';
2023-08-06 17:21:09 +01:00
2023-08-11 10:57:27 +01:00
const NoteList = (props: Props) => {
2023-08-12 15:45:19 +01:00
const [scrollTop, setScrollTop] = useState(0);
2023-08-11 18:09:12 +01:00
const listRenderer = defaultListRenderer;
if (listRenderer.flow !== ItemFlow.TopToBottom) throw new Error('Not implemented');
2023-08-06 17:21:09 +01:00
const itemSize: Size = useMemo(() => {
2023-08-11 18:09:12 +01:00
return listRenderer.itemSize;
}, [listRenderer.itemSize]);
2023-08-06 17:21:09 +01:00
2023-08-12 15:45:19 +01:00
const [startNoteIndex, endNoteIndex] = useVisibleRange(scrollTop, props.size, itemSize, props.notes.length);
const renderedNotes = useRenderedNotes(startNoteIndex, endNoteIndex, props.notes, props.selectedNoteIds, itemSize, listRenderer);
2023-08-06 17:21:09 +01:00
const noteItemStyle = useMemo(() => {
return {
width: 'auto',
height: itemSize.height,
};
}, [itemSize.height]);
const noteListStyle = useMemo(() => {
return {
width: props.size.width,
height: props.size.height,
};
}, [props.size]);
const onNoteClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
const noteId = event.currentTarget.getAttribute('data-note-id');
if (event.ctrlKey || event.metaKey) {
event.preventDefault();
props.dispatch({
type: 'NOTE_SELECT_TOGGLE',
id: noteId,
});
} else if (event.shiftKey) {
event.preventDefault();
props.dispatch({
type: 'NOTE_SELECT_EXTEND',
id: noteId,
});
} else {
props.dispatch({
type: 'NOTE_SELECT',
id: noteId,
});
}
}, [props.dispatch]);
2023-08-11 18:34:12 +01:00
useItemCss(listRenderer.itemCss);
2023-08-11 18:09:12 +01:00
2023-08-11 19:19:17 +01:00
const onItemContextMenu = useOnContextMenu(
props.selectedNoteIds,
props.selectedFolderId,
props.notes,
props.dispatch,
props.watchedNoteFiles,
props.plugins,
props.customCss
);
2023-08-12 15:45:19 +01:00
const onScroll = useCallback((event: any) => {
setScrollTop(event.target.scrollTop);
}, []);
const renderFiller = (key: string, height: number) => {
return <div key={key} style={{ height: height }}></div>;
};
const renderEmptyList = () => {
if (props.notes.length) return null;
return <div className="emptylist">{props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>;
};
2023-08-06 17:21:09 +01:00
const renderNotes = () => {
2023-08-12 15:45:19 +01:00
if (!props.notes.length) return null;
2023-08-06 17:21:09 +01:00
const output: JSX.Element[] = [];
2023-08-12 15:45:19 +01:00
output.push(renderFiller('top', startNoteIndex * itemSize.height));
for (let i = startNoteIndex; i <= endNoteIndex; i++) {
const note = props.notes[i];
const renderedNote = renderedNotes[note.id];
2023-08-06 17:21:09 +01:00
output.push(
2023-08-11 18:34:12 +01:00
<NoteListItem
2023-08-12 15:45:19 +01:00
key={note.id}
2023-08-06 17:21:09 +01:00
onClick={onNoteClick}
2023-08-11 18:09:12 +01:00
onChange={listRenderer.onChange}
2023-08-12 15:45:19 +01:00
noteId={note.id}
noteHtml={renderedNote ? renderedNote.html : ''}
2023-08-11 18:34:12 +01:00
itemSize={itemSize}
2023-08-06 17:21:09 +01:00
style={noteItemStyle}
2023-08-11 19:19:17 +01:00
onContextMenu={onItemContextMenu}
2023-08-06 17:21:09 +01:00
/>
);
}
2023-08-12 15:45:19 +01:00
output.push(renderFiller('bottom', (props.notes.length - endNoteIndex - 1) * itemSize.height));
2023-08-06 17:21:09 +01:00
return output;
};
return (
2023-08-12 15:45:19 +01:00
<div className="note-list" style={noteListStyle} onScroll={onScroll}>
{renderEmptyList()}
2023-08-06 17:21:09 +01:00
{renderNotes()}
</div>
);
};
const mapStateToProps = (state: AppState) => {
const selectedFolder: FolderEntity = state.notesParentType === 'Folder' ? BaseModel.byId(state.folders, state.selectedFolderId) : null;
const userId = state.settings['sync.userId'];
return {
notes: state.notes,
folders: state.folders,
selectedNoteIds: state.selectedNoteIds,
selectedFolderId: state.selectedFolderId,
themeId: state.settings.theme,
notesParentType: state.notesParentType,
searches: state.searches,
selectedSearchId: state.selectedSearchId,
watchedNoteFiles: state.watchedNoteFiles,
provisionalNoteIds: state.provisionalNoteIds,
isInsertingNotes: state.isInsertingNotes,
noteSortOrder: state.settings['notes.sortOrder.field'],
uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
showCompletedTodos: state.settings.showCompletedTodos,
highlightedWords: state.highlightedWords,
plugins: state.pluginService.plugins,
customCss: state.customCss,
focusedField: state.focusedField,
parentFolderIsReadOnly: state.notesParentType === 'Folder' && selectedFolder ? itemIsReadOnlySync(ModelType.Folder, ItemChange.SOURCE_UNSPECIFIED, selectedFolder as ItemSlice, userId, state.shareService) : false,
};
};
2023-08-11 10:57:27 +01:00
export default connect(mapStateToProps)(NoteList);