Skip to content

Commit 08e598f

Browse files
Merge pull request #8 from andyquinterom/T7
Adds footer to table
2 parents 66b2809 + 85a20eb commit 08e598f

File tree

10 files changed

+285
-101
lines changed

10 files changed

+285
-101
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: spyctable
22
Title: What the Package Does (One Line, Title Case)
3-
Version: 0.3.0
3+
Version: 0.4.0
44
Authors@R:
55
person("First", "Last", , "[email protected]", role = c("aut", "cre"),
66
comment = c(ORCID = "YOUR-ORCID-ID"))

NAMESPACE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
export(build_spyctable_html)
34
export(get_spyc_table_selection)
45
export(renderSpyCTable)
56
export(spyCTableOutput)
6-
export(spyc_header_create)
7+
export(spyctable)
78
useDynLib(spyctable, .registration = TRUE)

R/extendr-wrappers.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ NULL
1414
filter_from_values_vec <- function(values_vec, data) .Call(wrap__filter_from_values_vec, values_vec, data)
1515

1616
#' @export
17-
spyc_header_create <- function(names) .Call(wrap__spyc_header_create, names)
17+
build_spyctable_html <- function(data, names, nrow, format, na, id) .Call(wrap__build_spyctable_html, data, names, nrow, format, na, id)
1818

1919

2020
# nolint end

R/table.R

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
#' @export
2-
renderSpyCTable <- function(expr, env = parent.frame(), quoted = FALSE) {
2+
spyctable <- function(data, format = "default", na = "zero") {
3+
list(
4+
data = data,
5+
format = format,
6+
na = na
7+
)
8+
}
9+
10+
#' @export
11+
renderSpyCTable <- function(expr, env = parent.frame(), quoted = FALSE, id) {
312
func <- shiny::exprToFunction(expr, env, quoted)
13+
session <- shiny::getDefaultReactiveDomain()
414
shiny::reactive({
515
to_render <- func()
616
list(
7-
data = to_render,
8-
thead = jsonlite::unbox(spyc_header_create(colnames(to_render)))
17+
html = jsonlite::unbox(
18+
build_spyctable_html(
19+
to_render$data,
20+
colnames(to_render$data),
21+
nrow(to_render$data),
22+
to_render$format,
23+
to_render$na,
24+
id = shiny::getCurrentOutputInfo()$name
25+
)
26+
)
927
)
1028
})
1129
}
@@ -38,5 +56,5 @@ spyCTableOutput <- function(id, scroll_y = "50vh") {
3856

3957
#' @export
4058
get_spyc_table_selection <- function(input, dataset) {
41-
filter_from_values_vec(as.integer(input), dataset)
59+
filter_from_values_vec(as.character(input), dataset)
4260
}

inst/example/app.R

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,43 @@ library(shiny)
33
library(tidyselect)
44
library(dplyr)
55

6+
iris_char <- iris |>
7+
dplyr::mutate(Species = as.character(Species))
8+
9+
table_module_ui <- function(id) {
10+
ns <- shiny::NS(id)
11+
spyCTableOutput(ns("tabla"))
12+
}
13+
614
ui <- fluidPage(
715
theme = bslib::bs_theme(version = 5),
8-
actionButton("rerender", "Rerender"),
9-
spyCTableOutput("tabla")
16+
table_module_ui("my_module")
1017
)
1118

12-
char_iris <- 1:100 |>
13-
purrr::map_df(~ iris) |>
14-
dplyr::mutate(dplyr::across(tidyselect::everything(), as.character))
19+
table_module_server <- function(id) {
20+
shiny::moduleServer(id, function(input, output, session) {
21+
output$tabla <- renderSpyCTable({
22+
spyctable(
23+
iris_char,
24+
format = "default",
25+
na = "dash"
26+
)
27+
})
28+
29+
observe({
30+
print(
31+
get_spyc_table_selection(input$tabla_cells_selected, iris_char)
32+
)
33+
})
34+
35+
})
36+
}
37+
1538

1639
server <- function(input, output, session) {
1740

18-
output$tabla <- renderSpyCTable({
19-
char_iris
20-
}) |>
21-
bindEvent(input$rerender)
41+
table_module_server("my_module")
2242

23-
observe({
24-
print(
25-
get_spyc_table_selection(input$tabla_cells_selected, char_iris)
26-
)
27-
})
2843
}
2944

3045
shinyApp(ui, server)

inst/table/index.js

Lines changed: 10 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function enable_dragging() {
2020

2121
function selected_deselected(el) {
2222
// This is a pointer to the selection array
23-
const selection = globalSpyCTableIndex.get(el.tableId);
23+
const selection = globalSpyCTableIndex.get(el.dataset.table_id);
2424
let is_selected = el.classList.contains('selected');
2525
if (is_selected) {
2626
selection.delete(el);
@@ -33,19 +33,19 @@ function selected_deselected(el) {
3333
}
3434
spyCTableSelectionBuffer.length = 0;
3535
for (const element of selection) {
36-
spyCTableSelectionBuffer.push(element.coords);
36+
spyCTableSelectionBuffer.push(element.dataset.coords);
3737
}
38-
Shiny.setInputValue(el.inputId, spyCTableSelectionBuffer);
38+
Shiny.setInputValue(el.dataset.table_id + '_cells_selected', spyCTableSelectionBuffer);
3939
}
4040

41-
function mouse_over_event() {
41+
function mouse_over_event(el) {
4242
if (is_dragging) {
43-
selected_deselected(this)
43+
selected_deselected(el)
4444
}
4545
}
4646

47-
function mouse_down_event() {
48-
selected_deselected(this)
47+
function mouse_down_event(el) {
48+
selected_deselected(el)
4949
}
5050

5151
// This function is to deselect everything in the table
@@ -58,7 +58,7 @@ function spyctable_deselect_all(tableId) {
5858
}
5959
selection.clear();
6060
spyCTableSelectionBuffer.length = 0;
61-
Shiny.setInputValue(el.inputId, spyCTableSelectionBuffer);
61+
Shiny.setInputValue(tableId + '_cells_selected', spyCTableSelectionBuffer);
6262
}
6363
}
6464

@@ -68,43 +68,6 @@ addEventListener("mouseup", (_event) => {
6868
disable_dragging();
6969
});
7070

71-
function build_tbody(tableId, inputId, len_x, len_y, data, keys) {
72-
var tbody = document.createElement("tbody");
73-
74-
// If the user clicks then we enable dragging
75-
tbody.onmousedown = enable_dragging;
76-
77-
// If the user's mouse leaves the table we disable dragging
78-
tbody.onmouseleave = disable_dragging;
79-
80-
// Just in case, if the mouse just entered the table we
81-
// disable dragging aswell
82-
tbody.onmouseenter = disable_dragging;
83-
84-
const fragment = document.createDocumentFragment();
85-
86-
for (var i = 0; i < len_y; i++) {
87-
var current_row = document.createElement("tr");
88-
for (var c = 0; c < len_x; c++) {
89-
var current_cel = document.createElement("td");
90-
current_cel.coords = [c, i];
91-
current_cel.innerText = data[keys[c]][i];
92-
current_cel.classList.add("user-select-none");
93-
//We passed the pointer to every single cell
94-
current_cel.tableId = tableId;
95-
current_cel.onmouseover = mouse_over_event;
96-
current_cel.onmousedown = mouse_down_event;
97-
current_cel.inputId = inputId;
98-
current_row.appendChild(current_cel);
99-
}
100-
fragment.appendChild(current_row);
101-
}
102-
103-
tbody.appendChild(fragment);
104-
105-
return tbody;
106-
}
107-
10871
function fromHTML(html, trim = true) {
10972
// Process the HTML string.
11073
html = trim ? html.trim() : html;
@@ -137,16 +100,8 @@ spyCTableBinding.renderValue = function(el, msg) {
137100
selection = new Set();
138101
globalSpyCTableIndex.set(id, selection);
139102
}
140-
let data = msg.data;
141-
let thead_content = msg.thead;
142-
let keys = Object.keys(data);
143-
let len_x = keys.length;
144-
let len_y = data[keys[0]].length;
145-
var table = document.createElement("table");
146-
table.classList.add("table");
147-
table.id = id + '_inner_table';
148-
table.appendChild(fromHTML(thead_content));
149-
table.appendChild(build_tbody(id, inputId, len_x, len_y, data, keys));
103+
104+
var table = fromHTML(msg.html);
150105
el.appendChild(table);
151106

152107
let scroll_y = el.getAttribute("scroll-y");

src/rust/src/header.rs

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -188,39 +188,29 @@ impl<'a> ContainerBuilder<'a> {
188188
}
189189
Some((rowspan, colspan))
190190
}
191-
fn build(mut self) -> String {
192-
let mut buffer = Vec::<u8>::new();
193-
let _ = write!(&mut buffer, "<thead>");
191+
fn build(mut self, buffer: &mut Vec<u8>) {
192+
let _ = write!(buffer, "<thead>");
194193

195194
for row in 0..self.data.nrow {
196-
let _ = write!(&mut buffer, "<tr>");
195+
let _ = write!(buffer, "<tr>");
197196
for col in 0..self.data.ncol {
198197
if let Some((rowspan, colspan)) = self.render_th(row, col) {
199198
let _ = write!(
200-
&mut buffer,
199+
buffer,
201200
r#"
202201
<th colspan={colspan} rowspan={rowspan} class="border text-center align-middle">{}</th>
203202
"#,
204203
self.data.get(row, col).unwrap()
205204
);
206205
}
207206
}
208-
let _ = write!(&mut buffer, "</tr>");
207+
let _ = write!(buffer, "</tr>");
209208
}
210209

211-
let _ = write!(&mut buffer, "</thead>");
212-
213-
unsafe { String::from_utf8_unchecked(buffer) }
210+
let _ = write!(buffer, "</thead>");
214211
}
215212
}
216213

217-
/// @export
218-
#[extendr]
219-
fn spyc_header_create(names: Strings) -> String {
220-
ContainerBuilder::new(&names).build()
221-
}
222-
223-
extendr_module! {
224-
mod header;
225-
fn spyc_header_create;
214+
pub fn spyc_header_create(names: Strings, buffer: &mut Vec<u8>) {
215+
ContainerBuilder::new(&names).build(buffer);
226216
}

src/rust/src/lib.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use extendr_api::prelude::*;
2+
3+
use crate::tbody::{Formatting, NAFormatting};
4+
use std::io::Write;
25
mod header;
6+
mod tbody;
7+
mod tfoot;
38

49
fn list_from_vec_vec(values: Vec<Vec<&str>>, names: StrIter) -> List {
510
let mut result_list = List::from_iter(values.into_iter().map(|mut values| {
@@ -14,7 +19,7 @@ fn list_from_vec_vec(values: Vec<Vec<&str>>, names: StrIter) -> List {
1419
}
1520

1621
#[extendr]
17-
fn filter_from_values_vec(values_vec: Integers, data: List) -> List {
22+
fn filter_from_values_vec(values_vec: Strings, data: List) -> List {
1823
let mut result_list = Vec::new();
1924
result_list.resize_with(data.len(), Vec::new);
2025

@@ -24,22 +29,46 @@ fn filter_from_values_vec(values_vec: Integers, data: List) -> List {
2429

2530
let mut i = 0;
2631
while i < values_vec.len() {
27-
let x = values_vec[i].inner() as usize;
28-
let y = values_vec[i + 1].inner() as usize;
29-
if let Some(str_value) = data[x].index(y + 1).expect("Value must exists").as_str() {
30-
result_list[x].push(str_value);
32+
if let Some((x, y)) = values_vec[i].split_once(',') {
33+
let x: usize = x.parse().expect("X Must be a valid number");
34+
let y: usize = y.parse().expect("X Must be a valid number");
35+
if let Some(str_value) = data[x].index(y + 1).expect("Value must exists").as_str() {
36+
result_list[x].push(str_value);
37+
}
3138
}
32-
i += 2;
39+
i += 1;
3340
}
3441

3542
list_from_vec_vec(result_list, data.names().expect("Must have names"))
3643
}
3744

45+
/// @export
46+
#[extendr]
47+
fn build_spyctable_html(
48+
data: List,
49+
names: Strings,
50+
nrow: i32,
51+
format: Formatting,
52+
na: NAFormatting,
53+
id: &str,
54+
) -> String {
55+
let mut buffer = Vec::new();
56+
let _ = write!(
57+
&mut buffer,
58+
r#"<table id="{id}_inner_table" class="user-select-none table">"#
59+
);
60+
header::spyc_header_create(names, &mut buffer);
61+
tbody::build_tbody_and_foot(nrow, data, format, na, id, &mut buffer);
62+
let _ = write!(&mut buffer, "</table>");
63+
64+
unsafe { String::from_utf8_unchecked(buffer) }
65+
}
66+
3867
// Macro to generate exports.
3968
// This ensures exported functions are registered with R.
4069
// See corresponding C code in `entrypoint.c`.
4170
extendr_module! {
4271
mod spyctable;
43-
use header;
4472
fn filter_from_values_vec;
73+
fn build_spyctable_html;
4574
}

0 commit comments

Comments
 (0)