import * as React from 'react'; import { _ } from '@joplin/lib/locale'; import CommandService from '@joplin/lib/services/CommandService'; import { ChangeEvent, useCallback, useContext, useRef } from 'react'; import NoteToolbar from '../../NoteToolbar/NoteToolbar'; import { buildStyle } from '@joplin/lib/theme'; import time from '@joplin/lib/time'; import Setting from '@joplin/lib/models/Setting'; import { WindowIdContext } from '../../NewWindowOrIFrame'; interface Props { themeId: number; noteUserUpdatedTime: number; noteTitle: string; noteIsTodo: number; isProvisional: boolean; titleInputRef: React.RefObject; onTitleChange(event: ChangeEvent): void; disabled: boolean; } function styles_(props: Props) { return buildStyle(['NoteEditorTitleBar'], props.themeId, theme => { return { titleInput: { flex: 1, display: 'inline-block', paddingTop: 4, minHeight: 38, boxSizing: 'border-box', fontWeight: '700', paddingBottom: 4, paddingLeft: 0, paddingRight: 8, color: theme.textStyle.color, fontSize: Math.round(theme.textStyle.fontSize * 1.75), backgroundColor: 'transparent', border: 'none', outline: 'none', width: '100%', }, titleDate: { ...theme.textStyle, color: theme.colorFaded, paddingLeft: 8, whiteSpace: 'nowrap', }, toolbarStyle: { marginBottom: 0, minWidth: 0, }, }; }); } const useReselectHandlers = () => { const lastTitleFocus = useRef([0, 0]); const lastTitleValue = useRef(''); const onTitleBlur: React.FocusEventHandler = useCallback((event) => { const titleElement = event.currentTarget; lastTitleFocus.current = [titleElement.selectionStart, titleElement.selectionEnd]; lastTitleValue.current = titleElement.value; }, []); const onTitleFocus: React.FocusEventHandler = 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 }; }; export default function NoteTitleBar(props: Props) { const styles = styles_(props); const onTitleKeydown: React.KeyboardEventHandler = useCallback((event) => { const titleElement = event.currentTarget; const selectionAtEnd = titleElement.selectionEnd === titleElement.value.length; const isNavigationShortcut = (event.key === 'ArrowDown' && selectionAtEnd) || (event.key === 'Enter' && !event.shiftKey); const composing = event.nativeEvent.isComposing; // Don't change focus if the navigation shortcut is fired during composition. See // https://github.com/laurent22/joplin/issues/11485. if (!composing && isNavigationShortcut) { event.preventDefault(); const moveCursorToStart = event.key === 'ArrowDown' || event.key === 'Enter'; void CommandService.instance().execute('focusElement', 'noteBody', { moveCursorToStart }); } }, []); const { onTitleFocus, onTitleBlur } = useReselectHandlers(); function renderTitleBarDate() { const dateFormat = Setting.value('dateFormat'); const timeFormat = Setting.value('timeFormat') || 'HH:mm'; // 在中文语境或默认英式格式(DD/MM/YYYY)下,优先采用中文及国际标准时间格式 YYYY-MM-DD HH:mm let format = `${dateFormat} ${timeFormat}`; const isDefaultOrChinese = !dateFormat || dateFormat === Setting.DATE_FORMAT_1 || Setting.value('locale')?.toLowerCase().startsWith('zh'); if (isDefaultOrChinese) { format = `YYYY-MM-DD ${timeFormat}`; } return {time.formatMsToLocal(props.noteUserUpdatedTime, format)}; } const windowId = useContext(WindowIdContext); function renderNoteToolbar() { return ; } return (
{renderTitleBarDate()} {renderNoteToolbar()}
); }