Desktop: Accessibility: Replacing library used for datetime with native input element (#11725)

pull/11700/head^2
pedr 2025-01-27 12:50:38 -03:00 committed by GitHub
parent 5a3d57e39a
commit 8611391d01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 43 additions and 58 deletions

View File

@ -9,7 +9,6 @@ import shim from '@joplin/lib/shim';
import { NoteEntity } from '@joplin/lib/services/database/types';
import { focus } from '@joplin/lib/utils/focusHandler';
import Dialog from './Dialog';
const Datetime = require('react-datetime').default;
const { clipboard } = require('electron');
const formatcoords = require('formatcoords');
@ -48,6 +47,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
private styleKey_: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
private styles_: any;
private inputRef: React.RefObject<HTMLInputElement>;
public constructor(props: Props) {
super(props);
@ -55,6 +55,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
this.revisionsLink_click = this.revisionsLink_click.bind(this);
this.buttonRow_click = this.buttonRow_click.bind(this);
this.okButton = React.createRef();
this.inputRef = React.createRef();
this.state = {
formNote: null,
@ -224,13 +225,11 @@ class NotePropertiesDialog extends React.Component<Props, State> {
});
shim.setTimeout(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
if ((this.refs.editField as any).openCalendar) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
(this.refs.editField as any).openCalendar();
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
focus('NotePropertiesDialog::editPropertyButtonClick', (this.refs.editField as any));
// Opens datetime-local fields with calendar
if (this.inputRef.current.showPicker) {
this.inputRef.current.showPicker();
} else if (this.inputRef.current) {
focus('NotePropertiesDialog::editPropertyButtonClick', (this.inputRef.current));
}
}, 100);
}
@ -300,23 +299,14 @@ class NotePropertiesDialog extends React.Component<Props, State> {
if (this.state.editedKey === key) {
if (key.indexOf('_time') >= 0) {
controlComp = (
<Datetime
ref="editField"
initialValue={value}
dateFormat={time.dateFormat()}
timeFormat={time.timeFormat()}
inputProps={{
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
onKeyDown: (event: any) => onKeyDown(event),
style: styles.input,
}}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
onChange={(momentObject: any) => {
this.setState({ editedValue: momentObject });
}}
/>
);
controlComp = <input
type="datetime-local"
defaultValue={value}
ref={this.inputRef}
onChange={event => this.setState({ editedValue: event.target.value })}
onKeyDown={event => onKeyDown(event)}
style={styles.input}
/>;
editCompHandler = () => {
void this.saveProperty();
@ -328,7 +318,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
<input
defaultValue={value}
type="text"
ref="editField"
ref={this.inputRef}
onChange={event => {
this.setState({ editedValue: event.target.value });
}}

View File

@ -1,13 +1,13 @@
import * as React from 'react';
import { _ } from '@joplin/lib/locale';
import { themeStyle } from '@joplin/lib/theme';
import time from '@joplin/lib/time';
const Datetime = require('react-datetime').default;
import CreatableSelect from 'react-select/creatable';
import Select from 'react-select';
import makeAnimated from 'react-select/animated';
import { focus } from '@joplin/lib/utils/focusHandler';
import Dialog from './Dialog';
import { ChangeEvent } from 'react';
import { formatDateTimeLocalToMs, isValidDate } from '@joplin/utils/time';
interface Props {
themeId: number;
@ -204,16 +204,14 @@ export default class PromptDialog extends React.Component<Props, any> {
if (this.props.onClose) {
let outputAnswer = this.state.answer;
if (this.props.inputType === 'datetime') {
// outputAnswer = anythingToDate(outputAnswer);
outputAnswer = time.anythingToDateTime(outputAnswer);
outputAnswer = isValidDate(outputAnswer) ? formatDateTimeLocalToMs(outputAnswer) : null;
}
this.props.onClose(accept ? outputAnswer : null, buttonType);
}
this.setState({ visible: false, answer: '' });
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onChange = (event: any) => {
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
this.setState({ answer: event.target.value });
};
@ -226,11 +224,6 @@ export default class PromptDialog extends React.Component<Props, any> {
// return m.isValid() ? m.toDate() : null;
// }
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onDateTimeChange = (momentObject: any) => {
this.setState({ answer: momentObject });
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const onSelectChange = (newValue: any) => {
this.setState({ answer: newValue });
@ -258,8 +251,13 @@ export default class PromptDialog extends React.Component<Props, any> {
let inputComp = null;
if (this.props.inputType === 'datetime') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
inputComp = <Datetime className="datetime-picker" value={this.state.answer} inputProps={{ style: styles.input }} dateFormat={time.dateFormat()} timeFormat={time.timeFormat()} onChange={(momentObject: any) => onDateTimeChange(momentObject)} />;
inputComp = <input
defaultValue={this.state.answer}
onChange={onChange}
type="datetime-local"
className='datetime-picker'
style={styles.input}
/>;
} else if (this.props.inputType === 'tags') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
inputComp = <CreatableSelect className="tag-selector" onMenuOpen={this.select_menuOpen} onMenuClose={this.select_menuClose} styles={styles.select} theme={styles.selectTheme} ref={this.answerInput_} value={this.state.answer} placeholder="" components={makeAnimated()} isMulti={true} isClearable={false} backspaceRemovesValue={true} options={this.props.autocomplete} onChange={onSelectChange} onKeyDown={(event: any) => onKeyDown(event)} />;

View File

@ -4,6 +4,7 @@ import { _ } from '@joplin/lib/locale';
import { stateUtils } from '@joplin/lib/reducer';
import Note from '@joplin/lib/models/Note';
import time from '@joplin/lib/time';
import { formatMsToDateTimeLocal } from '@joplin/utils/time';
import { NoteEntity } from '@joplin/lib/services/database/types';
export const declaration: CommandDeclaration = {
@ -29,7 +30,7 @@ export const runtime = (comp: any): CommandRuntime => {
label: _('Set alarm:'),
inputType: 'datetime',
buttons: ['ok', 'cancel', 'clear'],
value: note.todo_due ? new Date(note.todo_due) : defaultDate,
value: note.todo_due ? formatMsToDateTimeLocal(note.todo_due) : formatMsToDateTimeLocal(defaultDate.getTime()),
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
onClose: async (answer: any, buttonType: string) => {
let newNote: NoteEntity = null;
@ -42,7 +43,7 @@ export const runtime = (comp: any): CommandRuntime => {
} else if (answer !== null) {
newNote = {
id: note.id,
todo_due: answer.getTime(),
todo_due: answer,
};
}

View File

@ -193,7 +193,6 @@
"pretty-bytes": "5.6.0",
"re-resizable": "6.9.17",
"react": "18.3.1",
"react-datetime": "3.2.0",
"react-dom": "18.3.1",
"react-redux": "8.1.3",
"react-select": "5.8.0",

View File

@ -24,7 +24,6 @@
// See https://sasscss.org/documentation/at-rules/import#plain-css-imports.
@import url('style/icons/style.css');
@import url('vendor/lib/@fortawesome/fontawesome-free/css/all.min.css');
@import url('vendor/lib/react-datetime/css/react-datetime.css');
@import url('vendor/lib/smalltalk/css/smalltalk.css');
@import url('vendor/lib/roboto-fontface/css/roboto/roboto-fontface.css');
@import url('vendor/lib/codemirror/lib/codemirror.css');

View File

@ -85,7 +85,6 @@ async function main() {
'codemirror/addon/dialog/dialog.css',
'codemirror/lib/codemirror.css',
'mark.js/dist/mark.min.js',
'react-datetime/css/react-datetime.css',
'roboto-fontface/css/roboto/roboto-fontface.css',
'smalltalk/css/smalltalk.css',
'smalltalk/dist/smalltalk.min.js',

View File

@ -145,3 +145,15 @@ export const formatMsToLocal = (ms: number, format: string|null = null) => {
if (format === null) format = dateTimeFormat();
return dayjs(ms).format(format);
};
export const formatMsToDateTimeLocal = (ms: number) => {
return formatMsToLocal(ms, 'YYYY-MM-DDTHH:mm');
};
export const isValidDate = (anything: string) => {
return dayjs(anything).isValid();
};
export const formatDateTimeLocalToMs = (anything: string) => {
return dayjs(anything).unix() * 1000;
};

View File

@ -8324,7 +8324,6 @@ __metadata:
pretty-bytes: 5.6.0
re-resizable: 6.9.17
react: 18.3.1
react-datetime: 3.2.0
react-dom: 18.3.1
react-redux: 8.1.3
react-select: 5.8.0
@ -39081,7 +39080,7 @@ __metadata:
languageName: node
linkType: hard
"prop-types@npm:^15.5.7, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2":
"prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2":
version: 15.7.2
resolution: "prop-types@npm:15.7.2"
dependencies:
@ -39576,18 +39575,6 @@ __metadata:
languageName: node
linkType: hard
"react-datetime@npm:3.2.0":
version: 3.2.0
resolution: "react-datetime@npm:3.2.0"
dependencies:
prop-types: ^15.5.7
peerDependencies:
moment: ^2.16.0
react: ^16.5.0 || ^17.0.0 || ^18.0.0
checksum: c3407beb64f44cd5944252bee1c4565fc138e3f1e81d8e4fd00d2aeac564a18848ee9af5f51fbbb39bba588f8c2d659570384af1ce8378d4e0049a6bdb083655
languageName: node
linkType: hard
"react-dev-utils@npm:^12.0.1":
version: 12.0.1
resolution: "react-dev-utils@npm:12.0.1"