Skip to content

Commit 214b67e

Browse files
authored
Merge pull request #41 from cynkra/f-implement-column-group
F implement column group
2 parents a3af719 + f0b4ca8 commit 214b67e

File tree

13 files changed

+32424
-13
lines changed

13 files changed

+32424
-13
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
export(cheetah)
44
export(cheetahOutput)
55
export(column_def)
6+
export(column_group)
67
export(renderCheetah)
78
import(htmlwidgets)
89
import(jsonlite)

R/cheetah.R

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#' @param data A data frame or matrix to display
66
#' @param columns A list of column definitions. Each column can be customized using
77
#' \code{column_def()}.
8+
#' @param column_group A list of column groups. Each group can be customized using
89
#' @param width Width of the widget
910
#' @param height Height of the widget
1011
#' @param elementId The element ID for the widget
@@ -30,6 +31,7 @@
3031
cheetah <- function(
3132
data,
3233
columns = NULL,
34+
column_group = NULL,
3335
width = NULL,
3436
height = NULL,
3537
elementId = NULL,
@@ -49,14 +51,21 @@ cheetah <- function(
4951
is_named_list(columns) & names(columns) %in% colnames(data)
5052
)
5153

54+
stopifnot(
55+
"If not NULL, `column_groups` must be a named list or list of named lists" =
56+
is.null(columns) |
57+
is_named_list(column_group) |
58+
all(unlist(lapply(column_group, is_named_list)))
59+
)
60+
5261
columns <-
5362
update_col_list_with_classes(data, columns) %>%
5463
make_table_sortable(sortable = sortable) %>%
5564
add_field_to_list()
5665

5766
data_json <- toJSON(data, dataframe = "rows")
5867
# forward options using x
59-
x <- list(data = data_json, columns = columns, search = search)
68+
x <- list(data = data_json, columns = columns, colGroup = column_group, search = search)
6069

6170
# create widget
6271
htmlwidgets::createWidget(

R/cheetah_utils.R

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,20 @@
6060
#' @param sort Whether to sort the column. Default to FALSE. May also be
6161
#' a JS callback to create custom logic (does not work yet).
6262
#'
63-
#' @export
6463
#' @return A list of column options to pass to the JavaScript API.
64+
#'
65+
#' @examples
66+
#' cheetah(
67+
#' iris,
68+
#' columns = list(
69+
#' Sepal.Length = column_def(name = "Length"),
70+
#' Sepal.Width = column_def(name = "Width"),
71+
#' Petal.Length = column_def(name = "Length"),
72+
#' Petal.Width = column_def(name = "Width")
73+
#' )
74+
#' )
75+
#'
76+
#' @export
6577
column_def <- function(
6678
name = NULL,
6779
width = NULL,
@@ -115,3 +127,41 @@ column_def <- function(
115127
sort = sort
116128
)
117129
}
130+
131+
#' Column group definitions
132+
#'
133+
#' Creates a column group definition for grouping columns in a Cheetah Grid widget.
134+
#'
135+
#' @param name Character string. The name to display for the column group.
136+
#' @param columns Character vector. The names of the columns to include in this group.
137+
#' @param header_style Named list of possibleCSS style properties to apply to the column group header.
138+
#'
139+
#' @return A list containing the column group definition.
140+
#'
141+
#' @examples
142+
#' cheetah(
143+
#' iris,
144+
#' columns = list(
145+
#' Sepal.Length = column_def(name = "Length"),
146+
#' Sepal.Width = column_def(name = "Width"),
147+
#' Petal.Length = column_def(name = "Length"),
148+
#' Petal.Width = column_def(name = "Width")
149+
#' ),
150+
#' column_group = list(
151+
#' column_group(name = "Sepal", columns = c("Sepal.Length", "Sepal.Width")),
152+
#' column_group(name = "Petal", columns = c("Petal.Length", "Petal.Width"))
153+
#' )
154+
#' )
155+
#'
156+
#' @export
157+
column_group <- function(name = NULL, columns, header_style = NULL) {
158+
column_style_check(header_style)
159+
160+
dropNulls(
161+
list(
162+
caption = name,
163+
columns = columns,
164+
headerStyle = header_style
165+
)
166+
)
167+
}

R/utils.R

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
is_testing <- function ()
2-
{
1+
is_testing <- function() {
32
identical(Sys.getenv("TESTTHAT"), "true")
43
}
54

@@ -92,7 +91,9 @@ update_col_list_with_classes <- function(data, col_list) {
9291
if (is.null(col_list[[col_name]]$columnType)) {
9392
if (col_classes[[col_name]] %in% c("numeric", "integer")) {
9493
col_list[[col_name]]$columnType <- "number"
95-
} else if (col_classes[[col_name]] == "factor" && any(in_shiny, is_testing)) {
94+
} else if (
95+
col_classes[[col_name]] == "factor" && any(in_shiny, is_testing)
96+
) {
9697
# This is to recover the possible choices for a factor column.
9798
menu_opt <- lapply(
9899
unique(data[[col_name]]),
@@ -117,12 +118,16 @@ check_action_type <- function(action = NULL, column_type = NULL) {
117118

118119
valid_actions <- c("input", "check", "radio", "inline_menu")
119120
if (!action %in% valid_actions) {
120-
stop("Invalid action type. Must be one of: ",
121-
paste(valid_actions, collapse = ", "))
121+
stop(
122+
"Invalid action type. Must be one of: ",
123+
paste(valid_actions, collapse = ", ")
124+
)
122125
}
123126

124127
# Validate action-column type compatibility
125-
if (action == "inline_menu" && any(is.null(column_type), column_type != "menu")) {
128+
if (
129+
action == "inline_menu" && any(is.null(column_type), column_type != "menu")
130+
) {
126131
stop("'inline_menu' action can only be used with 'menu' column type")
127132
}
128133
}

inst/htmlwidgets/cheetah.js

Lines changed: 32191 additions & 2 deletions
Large diffs are not rendered by default.

man/cheetah.Rd

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/column_def.Rd

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/column_group.Rd

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

srcjs/modules/utils.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
export function combineColumnsAndGroups(columnsList, colGroups) {
2+
// 1. Build a lookup by field
3+
const colsByField = {};
4+
columnsList.forEach(col => {
5+
colsByField[col.field] = col;
6+
});
7+
8+
// 2. Find each group's first member and all members
9+
const groupFirst = colGroups.map(g => g.columns[0]);
10+
const groupMembers = colGroups.reduce((acc, g) => acc.concat(g.columns), []);
11+
12+
const result = [];
13+
14+
// 3. Iterate in original order
15+
columnsList.forEach(col => {
16+
const f = col.field;
17+
const gi = groupFirst.indexOf(f);
18+
19+
if (gi !== -1) {
20+
// this is the first field of group gi → emit the group
21+
const grp = colGroups[gi];
22+
23+
// build nested column definitions
24+
const nested = grp.columns.map(fieldName => colsByField[fieldName]);
25+
26+
// extract everything except `columns` from grp
27+
const { columns, ...grpMeta } = grp;
28+
29+
result.push({ ...grpMeta, columns: nested });
30+
31+
} else if (groupMembers.includes(f)) {
32+
// a member of some group but not its first → skip
33+
34+
} else {
35+
// standalone column
36+
result.push(colsByField[f]);
37+
}
38+
});
39+
40+
return result;
41+
}
42+
43+

srcjs/widgets/cheetah.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'widgets';
2-
import { asHeader } from '../modules/header.js';
2+
import { combineColumnsAndGroups } from '../modules/utils.js';
33
import * as cheetahGrid from "cheetah-grid";
44

55
HTMLWidgets.widget({
@@ -50,6 +50,10 @@ HTMLWidgets.widget({
5050
columns = defaultCol;
5151
}
5252

53+
if (x.colGroup !== null) {
54+
columns = combineColumnsAndGroups(columns, x.colGroup);
55+
}
56+
5357
const grid = new cheetahGrid.ListGrid({
5458
parentElement: document.getElementById(id),
5559
header: columns,

0 commit comments

Comments
 (0)