diff --git a/frontend/components/CellInput.js b/frontend/components/CellInput.js index 06e63c9d75..50d4a5c969 100644 --- a/frontend/components/CellInput.js +++ b/frontend/components/CellInput.js @@ -480,7 +480,7 @@ export const CellInput = ({ pluto_actions.move_remote_cells([cell_id], pluto_actions.get_notebook().cell_order.indexOf(cell_id) + (direction === -1 ? -1 : 2)) // workaround for https://github.com/preactjs/preact/issues/4235 - // but the crollintoview behaviour is nice, also when the preact issue is fixed. + // but the scrollIntoView behaviour is nice, also when the preact issue is fixed. requestIdleCallback(() => { cm.dispatch({ // TODO: remove me after fix diff --git a/frontend/components/Editor.js b/frontend/components/Editor.js index b1a705eedd..b33f0e3acd 100644 --- a/frontend/components/Editor.js +++ b/frontend/components/Editor.js @@ -1581,9 +1581,9 @@ The notebook file saves every time you run a cell.` value=${notebook.in_temp_dir ? "" : notebook.path} on_submit=${this.submit_file_change} on_desktop_submit=${this.desktop_submit_file_change} + clear_on_blur=${true} suggest_new_file=${{ base: this.client.session_options?.server?.notebook_path_suggestion ?? "", - name: notebook.shortpath, }} placeholder="Save notebook..." button_label=${notebook.in_temp_dir ? "Choose" : "Move"} diff --git a/frontend/components/FilePicker.js b/frontend/components/FilePicker.js index c44be28d57..77fdab7a17 100644 --- a/frontend/components/FilePicker.js +++ b/frontend/components/FilePicker.js @@ -33,19 +33,12 @@ const assert_not_null = (x) => { } const set_cm_value = (/** @type{EditorView} */ cm, /** @type {string} */ value, scroll = true) => { - let had_focus_before = cm.hasFocus - cm.dispatch({ changes: { from: 0, to: cm.state.doc.length, insert: value }, selection: EditorSelection.cursor(value.length), // a long path like /Users/fons/Documents/article-test-1/asdfasdfasdfsadf.jl does not fit in the little box, so we scroll it to the left so that you can see the filename easily. scrollIntoView: scroll, }) - - if (!had_focus_before) { - // and blur the DOM again (because the previous transaction might have re-focused it) - cm.contentDOM.blur() - } } const is_desktop = !!window.plutoDesktop @@ -63,9 +56,10 @@ if (is_desktop) { * on_submit: (new_path: String) => Promise, * on_desktop_submit?: (loc?: string) => Promise, * client: import("../common/PlutoConnection.js").PlutoConnection, + * clear_on_blur: Boolean, * }} props */ -export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, on_submit, on_desktop_submit, client }) => { +export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, on_submit, on_desktop_submit, client, clear_on_blur }) => { const [is_button_disabled, set_is_button_disabled] = useState(true) const [url_value, set_url_value] = useState("") const forced_value = useRef("") @@ -100,7 +94,9 @@ export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, try { if (is_desktop && on_desktop_submit) { await on_desktop_submit((await guess_notebook_location(url_value)).path_or_url) - } else await on_submit(current_cm.state.doc.toString()) + } else { + await on_submit(current_cm.state.doc.toString()) + } current_cm.dom.blur() } catch (error) { set_cm_value(current_cm, forced_value.current, true) @@ -110,6 +106,19 @@ export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, return true } + const onBlur = (e) => { + const still_in_focus = base.current?.matches(":focus-within") || base.current?.contains(e.relatedTarget) + if (still_in_focus) return + const current_cm = cm.current + if (current_cm == null) return + if (clear_on_blur) + requestAnimationFrame(() => { + if (!current_cm.hasFocus) { + set_cm_value(current_cm, forced_value.current, true) + } + }) + } + const request_path_completions = () => { const current_cm = cm.current if (current_cm == null) return @@ -135,19 +144,12 @@ export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, setTimeout(() => { if (suggest_new_file) { suggest_not_tmp() - } else if (cm.state.doc.length === 0) { + } else { request_path_completions() } }, 0) return true }, - blur: (event, cm) => { - setTimeout(() => { - if (!cm.hasFocus) { - set_cm_value(cm, forced_value.current, true) - } - }, 200) - }, }), EditorView.updateListener.of((update) => { if (update.docChanged) { @@ -217,21 +219,6 @@ export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, mac: "Cmd-Shift-Enter", run: keyMapSubmit, }, - { - key: "Escape", - run: (cm) => { - assert_not_null(close_autocomplete_command).run(cm) - cm.dispatch({ - changes: { from: 0, to: cm.state.doc.length, insert: forced_value.current }, - selection: EditorSelection.cursor(value.length), - effects: EditorView.scrollIntoView(forced_value.current.length), - }) - // @ts-ignore - document.activeElement.blur() - return true - }, - preventDefault: true, - }, { key: "Tab", run: (cm) => { @@ -285,7 +272,7 @@ export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, ` : html` - + ` @@ -293,7 +280,7 @@ export const FilePicker = ({ value, suggest_new_file, button_label, placeholder, const pathhints = ({ client, suggest_new_file }) => - (ctx) => { + (/** @type {autocomplete.CompletionContext} */ ctx) => { const cursor = ctx.state.selection.main.to const oldLine = ctx.state.doc.toString() @@ -302,7 +289,7 @@ const pathhints = query: oldLine, }) .then((update) => { - const queryFileName = oldLine.split("/").pop().split("\\").pop() + const queryFileName = (oldLine.split("/").pop() ?? "").split("\\").pop() ?? "" const results = update.message.results const from = utf8index_to_ut16index(oldLine, update.message.start) diff --git a/frontend/components/welcome/Open.js b/frontend/components/welcome/Open.js index 3a0dc213c1..1c83eef732 100644 --- a/frontend/components/welcome/Open.js +++ b/frontend/components/welcome/Open.js @@ -43,18 +43,20 @@ export const Open = ({ client, connected, CustomPicker, show_samples, on_start_n value="" on_submit=${on_open_path} on_desktop_submit=${desktop_on_open_path} + clear_on_blur=${false} button_label=${window.plutoDesktop ? "Open File" : "Open"} placeholder=${picker.placeholder} /> - ${window.plutoDesktop && - html`<${FilePicker} - key=${picker.placeholder} - client=${client} - value="" - on_desktop_submit=${desktop_on_open_url} - button_label="Open from URL" - placeholder=${picker.placeholder} - />`} + ${window.plutoDesktop != null + ? html`<${FilePicker} + key=${picker.placeholder} + client=${client} + value="" + on_desktop_submit=${desktop_on_open_url} + button_label="Open from URL" + placeholder=${picker.placeholder} + />` + : null} ` }