2020-11-05 18:58:23 +02:00
|
|
|
import * as React from 'react';
|
2020-11-07 17:59:37 +02:00
|
|
|
import { _ } from '@joplin/lib/locale';
|
|
|
|
import CommandService from '@joplin/lib/services/CommandService';
|
2024-09-10 20:29:17 +02:00
|
|
|
import { ChangeEvent, useCallback, useRef } from 'react';
|
2020-11-05 18:58:23 +02:00
|
|
|
import NoteToolbar from '../../NoteToolbar/NoteToolbar';
|
2020-11-07 17:59:37 +02:00
|
|
|
import { buildStyle } from '@joplin/lib/theme';
|
|
|
|
import time from '@joplin/lib/time';
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
interface Props {
|
2020-11-12 21:29:22 +02:00
|
|
|
themeId: number;
|
|
|
|
noteUserUpdatedTime: number;
|
|
|
|
noteTitle: string;
|
|
|
|
noteIsTodo: number;
|
|
|
|
isProvisional: boolean;
|
2024-05-21 02:28:19 +02:00
|
|
|
titleInputRef: React.RefObject<HTMLInputElement>;
|
2020-11-12 21:29:22 +02:00
|
|
|
onTitleChange(event: ChangeEvent<HTMLInputElement>): void;
|
2023-07-16 18:42:42 +02:00
|
|
|
disabled: boolean;
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function styles_(props: Props) {
|
2024-04-11 09:35:20 +02:00
|
|
|
return buildStyle(['NoteEditorTitleBar'], props.themeId, theme => {
|
2020-11-05 18:58:23 +02:00
|
|
|
return {
|
|
|
|
titleInput: {
|
|
|
|
flex: 1,
|
|
|
|
display: 'inline-block',
|
|
|
|
paddingTop: 5,
|
2020-11-15 21:21:47 +02:00
|
|
|
minHeight: 38,
|
2020-11-05 18:58:23 +02:00
|
|
|
boxSizing: 'border-box',
|
|
|
|
fontWeight: 'bold',
|
|
|
|
paddingBottom: 5,
|
|
|
|
paddingLeft: 0,
|
|
|
|
paddingRight: 8,
|
|
|
|
color: theme.textStyle.color,
|
|
|
|
fontSize: Math.round(theme.textStyle.fontSize * 1.5),
|
|
|
|
backgroundColor: theme.backgroundColor,
|
|
|
|
border: 'none',
|
2020-12-09 01:27:11 +02:00
|
|
|
width: '100%',
|
2020-11-05 18:58:23 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
titleDate: {
|
|
|
|
...theme.textStyle,
|
|
|
|
color: theme.colorFaded,
|
2020-11-15 21:21:47 +02:00
|
|
|
paddingLeft: 8,
|
|
|
|
whiteSpace: 'nowrap',
|
2020-11-05 18:58:23 +02:00
|
|
|
},
|
|
|
|
toolbarStyle: {
|
|
|
|
marginBottom: 0,
|
2024-03-04 12:33:39 +02:00
|
|
|
minWidth: 0,
|
2020-11-05 18:58:23 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-09-10 20:29:17 +02:00
|
|
|
const useReselectHandlers = () => {
|
|
|
|
const lastTitleFocus = useRef([0, 0]);
|
|
|
|
const lastTitleValue = useRef('');
|
|
|
|
|
|
|
|
const onTitleBlur: React.FocusEventHandler<HTMLInputElement> = useCallback((event) => {
|
|
|
|
const titleElement = event.currentTarget;
|
|
|
|
lastTitleFocus.current = [titleElement.selectionStart, titleElement.selectionEnd];
|
|
|
|
lastTitleValue.current = titleElement.value;
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const onTitleFocus: React.FocusEventHandler<HTMLInputElement> = useCallback((event) => {
|
|
|
|
const titleElement = event.currentTarget;
|
|
|
|
// By default, focusing the note title bar can cause its content to become selected. We override
|
|
|
|
// this with a more reasonable default:
|
|
|
|
if (titleElement.selectionStart === 0 && titleElement.selectionEnd === titleElement.value.length) {
|
|
|
|
if (lastTitleValue.current !== titleElement.value) {
|
|
|
|
titleElement.selectionStart = titleElement.value.length;
|
|
|
|
} else {
|
|
|
|
titleElement.selectionStart = lastTitleFocus.current[0];
|
|
|
|
titleElement.selectionEnd = lastTitleFocus.current[1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return { onTitleBlur, onTitleFocus };
|
|
|
|
};
|
|
|
|
|
2020-11-12 21:13:28 +02:00
|
|
|
export default function NoteTitleBar(props: Props) {
|
2020-11-05 18:58:23 +02:00
|
|
|
const styles = styles_(props);
|
|
|
|
|
2024-08-27 19:05:48 +02:00
|
|
|
const onTitleKeydown: React.KeyboardEventHandler<HTMLInputElement> = useCallback((event) => {
|
|
|
|
const titleElement = event.currentTarget;
|
|
|
|
const selectionAtEnd = titleElement.selectionEnd === titleElement.value.length;
|
|
|
|
if ((event.key === 'ArrowDown' && selectionAtEnd) || event.key === 'Enter') {
|
2020-11-05 18:58:23 +02:00
|
|
|
event.preventDefault();
|
2024-08-27 19:05:48 +02:00
|
|
|
const moveCursorToStart = event.key === 'ArrowDown';
|
|
|
|
void CommandService.instance().execute('focusElement', 'noteBody', { moveCursorToStart });
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
}, []);
|
|
|
|
|
2024-09-10 20:29:17 +02:00
|
|
|
const { onTitleFocus, onTitleBlur } = useReselectHandlers();
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
function renderTitleBarDate() {
|
2021-05-17 20:33:44 +02:00
|
|
|
return <span className="updated-time-label" style={styles.titleDate}>{time.formatMsToLocal(props.noteUserUpdatedTime)}</span>;
|
2020-11-05 18:58:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function renderNoteToolbar() {
|
|
|
|
return <NoteToolbar
|
|
|
|
themeId={props.themeId}
|
|
|
|
style={styles.toolbarStyle}
|
2023-07-16 18:42:42 +02:00
|
|
|
disabled={props.disabled}
|
2020-11-05 18:58:23 +02:00
|
|
|
/>;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2024-10-11 23:03:41 +02:00
|
|
|
<div className='note-title-wrapper'>
|
2020-11-05 18:58:23 +02:00
|
|
|
<input
|
2021-05-17 20:33:44 +02:00
|
|
|
className="title-input"
|
2020-11-05 18:58:23 +02:00
|
|
|
type="text"
|
|
|
|
ref={props.titleInputRef}
|
2023-09-19 12:37:20 +02:00
|
|
|
placeholder={props.isProvisional ? (props.noteIsTodo ? _('Creating new to-do...') : _('Creating new note...')) : ''}
|
2020-11-05 18:58:23 +02:00
|
|
|
style={styles.titleInput}
|
2023-07-16 18:42:42 +02:00
|
|
|
readOnly={props.disabled}
|
2020-11-05 18:58:23 +02:00
|
|
|
onChange={props.onTitleChange}
|
|
|
|
onKeyDown={onTitleKeydown}
|
2024-09-10 20:29:17 +02:00
|
|
|
onFocus={onTitleFocus}
|
|
|
|
onBlur={onTitleBlur}
|
2020-11-05 18:58:23 +02:00
|
|
|
value={props.noteTitle}
|
|
|
|
/>
|
2024-10-11 23:03:41 +02:00
|
|
|
<div className='note-title-info-group'>
|
2020-11-15 21:21:47 +02:00
|
|
|
{renderTitleBarDate()}
|
|
|
|
{renderNoteToolbar()}
|
2024-10-11 23:03:41 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2020-11-05 18:58:23 +02:00
|
|
|
);
|
|
|
|
}
|