Skip to content

Commit

Permalink
🎞 Built-in Tables - interactive viewer (#646)
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsp authored Nov 11, 2020
1 parent 2e561e9 commit 08e9c66
Show file tree
Hide file tree
Showing 13 changed files with 358 additions and 58 deletions.
8 changes: 6 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name = "Pluto"
uuid = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
license = "MIT"
authors = ["Fons van der Plas <[email protected]>", "MikoΕ‚aj Bochenski <[email protected]>"]
version = "0.12.7"
version = "0.12.8"

[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Expand All @@ -16,16 +16,20 @@ MsgPack = "99f44e22-a591-53d1-9472-aa23ef4bd671"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[compat]
HTTP = "^0.8.18"
MsgPack = "1.1"
Tables = "1"
julia = "^1.0.0"

[extras]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Random"]
test = ["Test", "Random", "DataFrames", "OffsetArrays"]
22 changes: 19 additions & 3 deletions frontend/components/CellOutput.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { html, Component, useRef, useState, useLayoutEffect, useEffect } from "../imports/Preact.js"

import { ErrorMessage } from "./ErrorMessage.js"
import { TreeView } from "./TreeView.js"
import { TreeView, TableView } from "./TreeView.js"

import { connect_bonds } from "../common/Bond.js"
import { cl } from "../common/ClassTable.js"
Expand All @@ -13,7 +13,7 @@ export class CellOutput extends Component {
super()
this.old_height = 0
this.resize_observer = new ResizeObserver((entries) => {
const new_height = this.base.scrollHeight
const new_height = this.base.offsetHeight

// Scroll the page to compensate for change in page height:
if (document.body.querySelector("pluto-cell:focus-within")) {
Expand Down Expand Up @@ -44,7 +44,12 @@ export class CellOutput extends Component {
<pluto-output
class=${cl({
rich_output:
this.props.errored || !this.props.body || (this.props.mime !== "application/vnd.pluto.tree+object" && this.props.mime !== "text/plain"),
this.props.errored ||
!this.props.body ||
(this.props.mime !== "application/vnd.pluto.tree+object" &&
this.props.mime !== "application/vnd.pluto.table+object" &&
this.props.mime !== "text/plain"),
scroll_y: this.props.mime === "application/vnd.pluto.table+object" || this.props.mime === "text/plain",
})}
mime=${this.props.mime}
>
Expand Down Expand Up @@ -118,6 +123,17 @@ export const OutputBody = ({ mime, body, cell_id, all_completed_promise, request
/>
</div>`
break
case "application/vnd.pluto.table+object":
return html`<div>
<${TableView}
cell_id=${cell_id}
body=${body}
all_completed_promise=${all_completed_promise}
requests=${requests}
persist_js_state=${persist_js_state}
/>
</div>`
break
case "application/vnd.pluto.stacktrace+object":
return html`<div><${ErrorMessage} cell_id=${cell_id} requests=${requests} ...${body} /></div>`
break
Expand Down
5 changes: 3 additions & 2 deletions frontend/components/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -734,11 +734,12 @@ export class Editor extends Component {
}
})
},
reshow_cell: (cell_id, object_id) => {
reshow_cell: (cell_id, objectid, dim) => {
this.client.send(
"reshow_cell",
{
object_id: object_id,
objectid: objectid,
dim: dim,
},
{ notebook_id: this.state.notebook.notebook_id, cell_id: cell_id },
false
Expand Down
1 change: 0 additions & 1 deletion frontend/components/LiveDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ export let LiveDocs = ({ desired_doc_query, client, on_update_doc_query, noteboo
<button onClick=${(e) => {
set_state((state) => ({ ...state, hidden: true }))
e.stopPropagation()
console.log(state)
setTimeout(() => live_doc_search_ref.current && live_doc_search_ref.current.focus(), 0)
}}><span></span></button>
`}
Expand Down
80 changes: 69 additions & 11 deletions frontend/components/TreeView.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { html, useRef } from "../imports/Preact.js"
import { html, useRef, useState } from "../imports/Preact.js"

import { PlutoImage, RawHTMLContainer } from "./CellOutput.js"

Expand Down Expand Up @@ -42,6 +42,22 @@ const SimpleOutputBody = ({ mime, body, cell_id, all_completed_promise, requests
}
}

const More = ({ on_click_more }) => {
const [loading, set_loading] = useState(false)

return html`<jlmore
class=${loading ? "loading" : ""}
onclick=${(e) => {
if (!loading) {
if (on_click_more() !== false) {
set_loading(true)
}
}
}}
>more</jlmore
>`
}

export const TreeView = ({ mime, body, cell_id, all_completed_promise, requests, persist_js_state }) => {
const node_ref = useRef(null)
const onclick = (e) => {
Expand All @@ -60,6 +76,12 @@ export const TreeView = ({ mime, body, cell_id, all_completed_promise, requests,

self.classList.toggle("collapsed")
}
const on_click_more = () => {
if (node_ref.current.closest("jltree.collapsed") != null) {
return false
}
requests.reshow_cell(cell_id, body.objectid, 1)
}

const mimepair_output = (pair) => html`<${SimpleOutputBody}
cell_id=${cell_id}
Expand All @@ -69,16 +91,7 @@ export const TreeView = ({ mime, body, cell_id, all_completed_promise, requests,
requests=${requests}
persist_js_state=${persist_js_state}
/>`
const more = html`<r
><more
onclick=${(e) => {
if (node_ref.current.closest("jltree.collapsed") == null) {
requests.reshow_cell(cell_id, body.objectid)
}
}}
>more</more
></r
>`
const more = html`<r><${More} on_click_more=${on_click_more} /></r>`

var inner = null
switch (body.type) {
Expand Down Expand Up @@ -112,3 +125,48 @@ export const TreeView = ({ mime, body, cell_id, all_completed_promise, requests,

return html`<jltree class="collapsed" onclick=${onclick} ref=${node_ref}>${inner}</jltree>`
}

export const TableView = ({ mime, body, cell_id, all_completed_promise, requests, persist_js_state }) => {
const node_ref = useRef(null)

const mimepair_output = (pair) => html`<${SimpleOutputBody}
cell_id=${cell_id}
mime=${pair[1]}
body=${pair[0]}
all_completed_promise=${all_completed_promise}
requests=${requests}
persist_js_state=${persist_js_state}
/>`
const more = (dim) => html`<${More}
on_click_more=${() => {
requests.reshow_cell(cell_id, body.objectid, dim)
}}
/>`

const thead =
body.schema == null
? null
: html`<thead>
<tr class="schema-names">
${["", ...body.schema.names].map((x) => html`<th>${x === "more" ? more(2) : x}</th>`)}
</tr>
<tr class="schema-types">
${["", ...body.schema.types].map((x) => html`<th>${x === "more" ? null : x}</th>`)}
</tr>
</thead>`
const tbody = html`<tbody>
${body.rows.map(
(row) =>
html`<tr>
${row === "more"
? html`<td colspan="999">${more(1)}</td>`
: html`<th>${row[0]}</th>
${row[1].map((x) => html`<td>${x === "more" ? null : mimepair_output(x)}</td>`)}`}
</tr>`
)}
</tbody>`

return html`<table class="pluto-table">
${thead}${tbody}
</table>`
}
6 changes: 5 additions & 1 deletion frontend/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,11 @@ pluto-output {
background-color: white;
}

.scroll_y {
overflow-y: auto;
max-height: 80vh;
}

pluto-output:focus {
outline: none;
}
Expand Down Expand Up @@ -713,7 +718,6 @@ pluto-output > assignee:empty {

pluto-output > div {
flex-shrink: 0;
overflow-x: auto;
overflow-y: hidden;
}

Expand Down
70 changes: 62 additions & 8 deletions frontend/treeview.css
Original file line number Diff line number Diff line change
Expand Up @@ -122,30 +122,55 @@ jltree.collapsed r:last-child::after {
content: "";
}

jltree r > more {
jlmore {
display: inline-block;
margin: 0.6em 0em;
padding: 0.6em 0em;
cursor: pointer;
/* this only affects jlmore inside a table */
width: 100%;
}

jltree r > more::before {
jlmore::before {
margin-left: 0.2em;
margin-right: 0.5em;
top: -0.1em;
bottom: -0.1em;
display: inline-block;
position: relative;
content: "";
background-size: 100%;
height: 17px;
width: 17px;
height: 1em;
width: 1em;
opacity: 0.5;
background-image: url(https://cdn.jsdelivr.net/gh/ionic-team/[email protected]/src/svg/ellipsis-vertical.svg);
}

jltree.collapsed r > more {
jlmore.loading::before {
background-image: url(https://cdn.jsdelivr.net/gh/ionic-team/[email protected]/src/svg/sync-outline.svg);
animation: loadspin 3s ease-in-out infinite;
}

@keyframes loadspin {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(180deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(360deg);
}
100% {
transform: rotate(360deg);
}
}

jltree.collapsed jlmore {
margin: 0em;
}
jltree.collapsed r > more::before {
jltree.collapsed jlmore::before {
background-image: url(https://cdn.jsdelivr.net/gh/ionic-team/[email protected]/src/svg/ellipsis-horizontal.svg);
}

Expand Down Expand Up @@ -196,3 +221,32 @@ jlerror > section > ol > li > span {
opacity: 0.8;
padding: 0px 1em;
}

table.pluto-table {
table-layout: fixed;
}

table.pluto-table td {
max-width: 300px;
overflow: hidden;
}

table.pluto-table .schema-types {
color: rgba(0, 0, 0, 0.4);
font-family: "JuliaMono", monospace;
font-size: 0.75rem;
opacity: 0;
}

table.pluto-table thead:hover .schema-types {
opacity: 1;
}

table.pluto-table .schema-names {
transform: translate(0, 0.5em);
transition: transform 0.1s ease-in-out;
}

table.pluto-table thead:hover .schema-names {
transform: translate(0, 0);
}
2 changes: 1 addition & 1 deletion src/evaluation/WorkspaceManager.jl
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function eval_in_workspace(session_notebook::Union{Tuple{ServerSession,Notebook}
nothing
end

function format_fetch_in_workspace(session_notebook::Union{Tuple{ServerSession,Notebook},Workspace}, cell_id, ends_with_semicolon, showmore_id::Union{PlutoRunner.ObjectID, Nothing}=nothing)
function format_fetch_in_workspace(session_notebook::Union{Tuple{ServerSession,Notebook},Workspace}, cell_id, ends_with_semicolon, showmore_id::Union{PlutoRunner.ObjectDimPair, Nothing}=nothing)
workspace = get_workspace(session_notebook)

# instead of fetching the output value (which might not make sense in our context, since the user can define structs, types, functions, etc), we format the cell output on the worker, and fetch the formatted output.
Expand Down
Loading

2 comments on commit 08e9c66

@fonsp
Copy link
Owner Author

@fonsp fonsp commented on 08e9c66 Nov 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/24479

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.12.8 -m "<description of version>" 08e9c66c42828160e3be936a4d96b503c5e20c8e
git push origin v0.12.8

Please sign in to comment.