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

Added a new widget type - Interactive Table. #282

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
115 changes: 106 additions & 9 deletions js_dependencies/table.css
Original file line number Diff line number Diff line change
@@ -1,26 +1,123 @@
/* ============================== */
/* Default-theme */
/* ============================== */
table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
color: #000000;
}

table td,
table th {
padding: 1px 5px;
resize: horizontal;
/* Drag-and-drop column widths */
overflow: auto;
min-width: 100px;
/* Give an initial column width */
border: 1px solid #dddddd;
/* add a border */
}

table tr:nth-child(even) {
background-color: #f2f2f2;
}

td, th, tr {
table tr:hover {
background-color: #dddddd;
}

table th {
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
background-color: #e2e2e2;
color: #000000;
/* font color */
}


/* ============================== */
/* Light-theme */
/* ============================== */
.light-theme table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
color: #000000;
}

.light-theme table td,
.light-theme table th {
padding: 1px 5px;
resize: horizontal; /* Drag-and-drop column widths */
overflow: auto;
min-width: 100px; /* Give an initial column width */
border: 1px solid #dddddd; /* add a border */
}

tr:nth-child(even) {
.light-theme table tr:nth-child(even) {
background-color: #f2f2f2;
}

tr:hover {
background-color: #ddd;
.light-theme table tr:hover {
background-color: #dddddd;
}

th {
.light-theme table th {
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
background-color: #e2e2e2;
color: white;
color: #000000; /* font color */
}

.light-theme table input {
background-color: rgba(255, 255, 255, 0.5);
color: #000; /* font color */
font-size: 14px;
}


/* ============================== */
/* Dark-theme */
/* ============================== */
.dark-theme table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
color: #ffffff;
background-color: #2c2c2c;
}

.dark-theme table td,
.dark-theme table th {
padding: 1px 5px;
resize: horizontal;
overflow: auto;
min-width: 100px;
border: 1px solid #555555;
}

.dark-theme table tr:nth-child(even) {
background-color: #3a3a3a;
}

.dark-theme table tr:hover {
background-color: #444444;
}

.dark-theme table th {
padding-top: 10px;
padding-bottom: 10px;
text-align: left;
background-color: #555555;
color: #ffffff;
}

.dark-theme table input {
background-color: rgba(255, 255, 255, 0);
color: #fff; /* font color */
font-size: 14px;
}
79 changes: 79 additions & 0 deletions src/widgets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,85 @@ function jsrender(session::Session, table::Table)
)
end

"""
An interactive table that conform to the Tables.jl Table interface,
which gets rendered nicely!
"""
struct InteractTable
table
cell_obs::Vector{Vector{Observable}}
colnames::Tuple
coltypes::Tuple
end

"""
Constructor for the interactive table that is editable and comes with automatic writeback. Frontend input can be synchronized to the backend (but not vice versa).
"""
function InteractTable(tbl)
# construct nrows×ncols cell_obs::Observable
schema = Tables.schema(tbl)
colnames, coltypes = schema.names, schema.types
coltable = Tables.columntable(tbl)
ncols = length(colnames)
colvec1 = getproperty(coltable, colnames[1])
@assert colvec1 isa AbstractVector "The value of the table is not a vector."
nrows = length(colvec1)

cell_obs = [Vector{Observable}(undef, nrows) for _ in 1:ncols]
for (j, colvec) in enumerate(values(Tables.columns(coltable)))
for (i, val) in enumerate(colvec)
cell_obs[j][i] = Observable(val)
on(cell_obs[j][i]) do newval
colvec[i] = newval
end
end
end

return InteractTable(tbl, cell_obs, colnames, coltypes)
end

function jsrender(session::Session, table::InteractTable)
(; cell_obs, colnames, coltypes) = table
header = DOM.thead(DOM.tr(DOM.th.(string.(colnames))...))
ncols, nrows = length(colnames), length(cell_obs[1])

# Construct an editable <tbody>
tr_nodes = Vector{Hyperscript.Node{Hyperscript.HTMLSVG}}(undef, nrows)
for i in 1:nrows
td_nodes = Vector{Hyperscript.Node{Hyperscript.HTMLSVG}}(undef, ncols)
for j in 1:ncols
obs = cell_obs[j][i]
coltype = coltypes[j]
if coltype <: Number
input_type = "number"
parse_js = js""" e => {
const val = parseFloat(e.target.value);
if (!isNaN(val)) {
$(obs).notify(val);
}}
"""
else
input_type = "text"
parse_js = js"e => {$(obs).notify(e.target.value);}"
end
# <input "text/number">
input_el = DOM.input(; type=input_type, value=obs, onchange=parse_js)
# Use onjs to listen to cell_obs (backend), synchronizing the frontend input_el.value when it changes.
onjs(session, obs, js"v => event.srcElement.value = String(v)")

td_nodes[j] = DOM.td(input_el)
end
tr_nodes[i] = DOM.tr(td_nodes...)
end

body = DOM.tbody(tr_nodes...)

return DOM.div(
jsrender(session, Asset(Bonito.dependency_path("table.css"))),
DOM.table(header, body),
)
end

struct CodeEditor
theme::String
language::String
Expand Down