Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pluto ux process file drop #707

Merged
merged 33 commits into from
Dec 30, 2020
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
70522da
Add Support for file drag & drop
Nov 22, 2020
05ad525
Interoperability with Drop Ruler
Nov 22, 2020
8a48f6b
Save file and get path in the front-end
Nov 23, 2020
0fb00ea
Working POC
Nov 23, 2020
b5f5ea0
Add 'raw' before path string literals
Nov 24, 2020
7ca9d56
Move code templates to Julia
Nov 24, 2020
773eda8
Change Image sample to use Images
Nov 24, 2020
88820f9
Use TableIOInterface for Files -> Table conversions
Nov 25, 2020
5fb007c
Move drop handler out of Cell.js
Nov 25, 2020
e708577
Update the code for images & text to use let block
Nov 25, 2020
43e8fd1
Make file transfer !!!25%!!! FASTER dropping base64 usage
Nov 25, 2020
e2eb8fb
Use correct is_extension_supported
Nov 26, 2020
7f21590
Handle non-empty cells
Nov 27, 2020
21941bd
Rename hook, var for debounce ms
Nov 29, 2020
54a5f0f
Prevent error message while saving
Nov 30, 2020
7c64753
Make JavaScripty names more Plutonic
Nov 30, 2020
4da1b55
update the handling of Juia files
Nov 30, 2020
de9c46c
Move assets folder along with notebook
Dec 3, 2020
2c286b3
Don't run file event when moving cells
Dec 5, 2020
218f21f
Save files with the same name with incremental numbers
Dec 5, 2020
d2706cd
make warning message less aggressive
Dec 5, 2020
81d81be
🦆 Fix expression equality again
fonsp Dec 9, 2020
fdf8bcb
Revert "🦆 Fix expression equality again"
fonsp Dec 9, 2020
ad61515
Fix generated path code by lungben
Dec 11, 2020
dbd4f3b
Add statistics
Dec 11, 2020
e62adb5
Merge branch 'master' of github.com:fonsp/Pluto.jl into PlutoUX-Proce…
Dec 22, 2020
62aaee4
Benjamin's compat suggestion
Dec 22, 2020
15b3dfd
Merge branch 'master' of github.com:fonsp/Pluto.jl into PlutoUX-Proce…
Dec 30, 2020
e2527a3
Use the latest dralbase
Dec 30, 2020
20c0c8c
fix 1 bug, handle drops on body
Dec 30, 2020
4c5e9e7
fix typo
Dec 30, 2020
c0d5e0b
Fix frontend tests thanks)
Dec 30, 2020
7c69e92
don't change whitespace!
Dec 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ MsgPack = "99f44e22-a591-53d1-9472-aa23ef4bd671"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
TableIOInterface = "d1efa939-5518-4425-949f-ab857e148477"
fonsp marked this conversation as resolved.
Show resolved Hide resolved
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

Expand Down
14 changes: 13 additions & 1 deletion frontend/components/Cell.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { html, useState, useEffect, useLayoutEffect, useRef } from "../imports/Preact.js"
import { html, useState, useEffect, useMemo, useRef } from "../imports/Preact.js"

import { CellOutput } from "./CellOutput.js"
import { CellInput } from "./CellInput.js"
import { RunArea, useMillisSinceTruthy } from "./RunArea.js"
import { cl } from "../common/ClassTable.js"
import { useDropHandler } from "./useDropHandler.js"

/**
* @typedef {Object} CodeState
Expand Down Expand Up @@ -92,6 +93,9 @@ export const Cell = ({
}) => {
// cm_forced_focus is null, except when a line needs to be highlighted because it is part of a stack trace
const [cm_forced_focus, set_cm_forced_focus] = useState(null)
const { saving_file, drag_active, event_handler, inactive_handler } = useDropHandler(requests, on_change, cell_id)
const has_code = local_code.body?.length > 0
const handler = !has_code ? event_handler : inactive_handler
const localTimeRunning = 10e5 * useMillisSinceTruthy(running)
useEffect(() => {
const focusListener = (e) => {
Expand Down Expand Up @@ -120,13 +124,20 @@ export const Cell = ({

return html`
<pluto-cell
onDragOver=${handler}
onDrop=${handler}
onDragEnter=${handler}
onDragLeave=${handler}
class=${cl({
queued: queued,
running: running,
errored: errored,
selected: selected,
code_differs: class_code_differs,
code_folded: class_code_folded,
drop_target: drag_active,
drop_invalid: has_code && !saving_file,
saving_file: saving_file,
})}
id=${cell_id}
>
Expand Down Expand Up @@ -163,6 +174,7 @@ export const Cell = ({
scroll_into_view_after_creation=${scroll_into_view_after_creation}
cm_forced_focus=${cm_forced_focus}
set_cm_forced_focus=${set_cm_forced_focus}
on_drag_drop_events=${event_handler}
on_submit=${(new_code) => {
requests.change_remote_cell(cell_id, new_code)
}}
Expand Down
28 changes: 27 additions & 1 deletion frontend/components/CellInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const CellInput = ({
on_change,
on_update_doc_query,
on_focus_neighbor,
on_drag_drop_events,
client,
cell_id,
notebook_id,
Expand Down Expand Up @@ -310,6 +311,25 @@ export const CellInput = ({
}
return true
}

cm.on("dragover", (cm_, e) => {
on_drag_drop_events(e)
return true
})
cm.on("drop", (cm_, e) => {
on_drag_drop_events(e)
e.preventDefault()
return true
})
cm.on("dragenter", (cm_, e) => {
on_drag_drop_events(e)
return true
})
cm.on("dragleave", (cm_, e) => {
on_drag_drop_events(e)
return true
})

cm.on("cursorActivity", () => {
setTimeout(() => {
if (!cm.hasFocus()) return
Expand Down Expand Up @@ -387,12 +407,18 @@ export const CellInput = ({
}, [])

useEffect(() => {
if (!remote_code.submitted_by_me) {
if (!remote_code.submitted_by_me || cm_ref.current.value !== local_code.body) {
cm_ref.current.setValue(remote_code.body)
}
cm_ref.current.options.disableInput = disable_input
}, [remote_code.timestamp])

useEffect(() => {
if (cm_ref.current.value !== local_code.body && local_code.body === remote_code.body) {
cm_ref.current.setValue(remote_code.body)
}
}, [local_code.body, remote_code.body])

useEffect(() => {
if (cm_forced_focus == null) {
clear_selection(cm_ref.current)
Expand Down
7 changes: 7 additions & 0 deletions frontend/components/DropRuler.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,20 @@ export class DropRuler extends Component {
}
})
document.addEventListener("dragenter", (e) => {
if (e.dataTransfer.types[0] !== "text/pluto-cell") return
if (!this.state.drag_target) this.precompute_cell_edges()
this.lastenter = e.target
this.setState({ drag_target: true })
})
document.addEventListener("dragleave", (e) => {
if (e.dataTransfer.types[0] !== "text/pluto-cell") return
if (e.target === this.lastenter) {
this.setState({ drag_target: false })
}
})
document.addEventListener("dragover", (e) => {
// Called continuously during drag
if (e.dataTransfer.types[0] !== "text/pluto-cell") return
this.mouse_position = e

this.setState({
Expand All @@ -76,6 +79,10 @@ export class DropRuler extends Component {
})
document.addEventListener("drop", (e) => {
// Guaranteed to fire before the 'dragend' event
// Ignore files
if (e.dataTransfer.types[0] !== "text/pluto-cell") {
return
}
this.setState({
drag_target: false,
})
Expand Down
11 changes: 11 additions & 0 deletions frontend/components/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,17 @@ export class Editor extends Component {

// these are things that can be done to the remote notebook
this.requests = {
write_file: (cell_id, { file, name, type }) => {
return this.client.send(
"write_file",
{ file, name, type, path: this.state.notebook.path },
{
notebook_id: this.state.notebook.notebook_id,
cell_id: cell_id,
},
true
)
},
change_remote_cell: (cell_id, new_code, create_promise = false) => {
this.counter_statistics.numEvals++
// set_cell_state(cell_id, { running: true })
Expand Down
105 changes: 105 additions & 0 deletions frontend/components/useDropHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useState, useMemo } from "../imports/Preact.js"

const MAGIC_TIMEOUT = 500
const DEBOUNCE_MAGIC_MS = 250

const prepareFile = (file) =>
new Promise((resolve, reject) => {
const { name, type } = file
const fr = new FileReader()
fr.onerror = () => reject("Failed to read file!")
fr.onloadstart = () => {}
fr.onprogress = ({ loaded, total }) => {}
fr.onload = () => {}
fr.onloadend = ({ target: { result } }) => resolve({ file: result, name, type })
fr.readAsArrayBuffer(file)
})

export const useDropHandler = (requests, on_change, cell_id) => {
const [saving_file, set_saving_file] = useState(false)
const [drag_active, set_drag_active_fast] = useState(false)
const set_drag_active = useMemo(() => _.debounce(set_drag_active_fast, DEBOUNCE_MAGIC_MS), [set_drag_active_fast])
const inactive_handler = useMemo(
() => (ev) => {
if (ev.dataTransfer.types[0] === "text/pluto-cell") return
switch (ev.type) {
case "drop":
ev.preventDefault() // don't file open
break
case "dragover":
ev.preventDefault()
ev.dataTransfer.dropEffect = "none"
set_drag_active(true)
setTimeout(() => set_drag_active(false), MAGIC_TIMEOUT)
break
case "dragenter":
set_drag_active_fast(true)
break
case "dragleave":
set_drag_active(false)
break
default:
break
}
},
[set_drag_active, set_drag_active_fast]
)
const event_handler = useMemo(() => {
const uploadAndCreateCodeTemplate = async (file) => {
if (!(file instanceof File)) return " # File can't be read"
set_saving_file(true)
const {
message: { success, code },
} = await prepareFile(file).then(
(preparedObj) => {
return requests.write_file(cell_id, preparedObj)
},
() => alert("Pluto can't save this file 😥")
)
set_saving_file(false)
set_drag_active_fast(false)
if (!success) {
alert("Pluto can't save this file 😥")
return "# File save failed"
}
if (code) return code
alert("Pluto doesn't know what to do with this file 😥. Feel that's wrong? Open an issue!")
return ""
}
return (ev) => {
// dataTransfer is in Protected Mode here. see type, let Pluto DropRuler handle it.
if (ev.dataTransfer.types[0] === "text/pluto-cell") return
switch (ev.type) {
case "cmdrop":
case "drop":
ev.preventDefault() // don't file open
set_drag_active(false)
if (!ev.dataTransfer.files.length) {
return
}
uploadAndCreateCodeTemplate(ev.dataTransfer.files[0]).then((code) => {
if (code) {
on_change(code)
requests.change_remote_cell(cell_id, code)
}
})
break
case "dragover":
ev.preventDefault()
ev.dataTransfer.dropEffect = "copy"
set_drag_active(true)
setTimeout(() => set_drag_active(false), MAGIC_TIMEOUT)
break
case "dragenter":
set_drag_active_fast(true)
break
case "dragleave":
set_drag_active(false)
break
default:
}
}
}, [set_drag_active, set_drag_active_fast, set_saving_file, requests, cell_id, on_change])

return { saving_file, drag_active, event_handler, inactive_handler }
}
31 changes: 31 additions & 0 deletions frontend/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -989,6 +989,37 @@ pluto-cell.running.errored > pluto-trafficlight::after {
background-size: 4px var(--patternHeight); /* 16 * sqrt(2) */
}

pluto-cell.drop_target {
position: relative;
}

pluto-cell.drop_target pluto-input::after {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
color: black;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
background-color: rgba(255, 255, 255, 0.75);
content: "+ Drop file here";
}

pluto-cell.drop_target.saving_file pluto-input::after {
content: "The 💾 File you dropped is being saved... ";
color: white;
background-color: rgba(3, 92, 151, 0.75);
}

pluto-cell.drop_target.drop_invalid pluto-input::after {
background-color: #ece5e5bf;
color: rgba(0, 0, 0, 0.85);
content: "Try dropping your file in an empty cell!";
}

/* Define --patternHeight for this keyframes animation to work! */
@keyframes scrollbackground {
0% {
Expand Down
7 changes: 5 additions & 2 deletions src/notebook/Notebook.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function save_notebook(io, notebook::Notebook)
print(io, c.code)
print(io, _cell_suffix)
end

println(io, _cell_id_delimiter, "Cell order:")
for c in notebook.cells
delim = c.code_folded ? _order_delimiter_folded : _order_delimiter
Expand All @@ -107,7 +107,7 @@ function save_notebook(notebook::Notebook, path::String)
open(path, "w") do io
save_notebook(io, notebook)
end
end
end

save_notebook(notebook::Notebook) = save_notebook(notebook, notebook.path)

Expand Down Expand Up @@ -231,5 +231,8 @@ function move_notebook!(notebook::Notebook, newpath::String)
if oldpath_tame != newpath_tame
rm(oldpath_tame)
end
if isdir("$oldpath_tame.assets")
mv("$oldpath_tame.assets", "$newpath_tame.assets")
end
notebook
end
Loading