Skip to content

Commit

Permalink
Use AnyWidget for resizeable widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
melff committed Dec 14, 2024
1 parent abb5041 commit 71faf22
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 44 deletions.
5 changes: 5 additions & 0 deletions pkg/R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,8 @@ menu_ <- function(choices,title=NULL,...) {
install_menu <- function(){
replace_in_package("utils", "menu", menu_)
}

read_asset <- function(path) {
path <- system.file(path,package="RKernel")
paste(readLines(path),collapse="\n")
}
47 changes: 22 additions & 25 deletions pkg/R/widget-resizeable.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# resizeable_css <- readLines(system.file("css/resizeable.css",
# package="RKernel"))
# resizeable_esm <- readLines(system.file("js/resizeable.js",
# package="RKernel"))

#' Resizeable Widgets
#' @description R6 classes of resizeable containers for arbitrary HTML code
#' @details Support for resizeable widgets, available from
Expand All @@ -8,28 +13,24 @@
#' @export
ResizeableWidgetClass <- R6Class_(
"ResizeableWidget",
inherit = DOMWidgetClass,
inherit = AnyWidgetClass,
public = list(
#' @field _view_name Name of the Javascript model view in the frontend
`_view_name` = structure(Unicode("ResizeableView"),sync=TRUE),
#' @field _model_name Name of the Javascript model in the frontend
`_model_name` = structure(Unicode("ResizeableModel"),sync=TRUE),
#' @field _view_module Name of the module where the view is defined
`_view_module` = structure(Unicode("resizeable-widget"),sync=TRUE),
#' @field _model_module Name of the Javascript module with the model
`_model_module` = structure(Unicode("resizeable-widget"),sync=TRUE),
#' @field _view_module_version Version of the module where the view is defined
`_view_module_version` = structure(Unicode("^0.1.0"),sync=TRUE),
#' @field _model_module_version Version of the module where the model is defined
`_model_module_version` = structure(Unicode("^0.1.0"),sync=TRUE),
#' @field _anywidget_id The AnyWidget id
`_anywidget_id` = structure(Unicode("ResizeableWidget"), sync = TRUE),
#' @field _css A Unicode trait with relevant CSS code
`_css` = structure(Unicode(read_asset("css/resizeable.css")), sync = TRUE),
#' @field _esm A Unicode trait with relevant Javascript code
`_esm` = structure(Unicode(read_asset("js/resizeable.js")), sync = TRUE),
#' @field width (Current) width of the widget
width = structure(Integer(-1),sync = TRUE),
width = structure(Integer(-1), sync = TRUE),
#' @field height (Current) height of the widget
height = structure(Integer(-1),sync = TRUE),
height = structure(Integer(-1), sync = TRUE),
#' @field direction Which direction(s) should the widget be resizeable
direction = structure(Unicode("both"), sync = TRUE),
#' @field value (Current) HTML code content of the widget
value = structure(Unicode("<em>Hello World!</em>"),sync = TRUE),
value = structure(Unicode("<em>Hello World!</em>"), sync = TRUE),
#' @field debug Whether to show a dashed frame and background color for debugging
debug = structure(Boolean(FALSE),sync = TRUE)
debug = structure(Boolean(FALSE), sync = TRUE)
)
)

Expand All @@ -38,10 +39,8 @@ VResizeableWidgetClass <- R6Class_(
"VResizeableWidget",
inherit = ResizeableWidgetClass,
public = list(
#' @field _view_name Name of the Javascript model view in the frontend
`_view_name` = structure(Unicode("VResizeableView"),sync=TRUE),
#' @field _model_name Name of the Javascript model in the frontend
`_model_name` = structure(Unicode("VResizeableModel"),sync=TRUE)
#' @field direction Which direction(s) should the widget be resizeable
direction = structure(Unicode("vertical"), sync = TRUE)
)
)

Expand All @@ -50,10 +49,8 @@ HResizeableWidgetClass <- R6Class_(
"HResizeableWidget",
inherit = ResizeableWidgetClass,
public = list(
#' @field _view_name Name of the Javascript model view in the frontend
`_view_name` = structure(Unicode("HResizeableView"),sync=TRUE),
#' @field _model_name Name of the Javascript model in the frontend
`_model_name` = structure(Unicode("HResizeableModel"),sync=TRUE)
#' @field direction Which direction(s) should the widget be resizeable
direction = structure(Unicode("horizontal"), sync = TRUE)
)
)

Expand Down
7 changes: 7 additions & 0 deletions pkg/inst/css/resizeable.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.resizer { display:flex; margin:0; padding:0; resize:both; overflow:hidden }
.vresizer { display:flex; margin:0; padding:0; resize:vertical; overflow:hidden }
.hresizer { display:flex; margin:0; padding:0; resize:horizontal; overflow:hidden }
.resizer > .resized,
.hresizer > .resized,
.vresizer > .resized { flex-grow:1; margin:0; padding:0; border:0 }
.debug-resizer { background:rgb(115, 210, 170); border:4px dashed black; }
64 changes: 64 additions & 0 deletions pkg/inst/js/resizeable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
function render({ model, el }) {
let _innerdiv = document.createElement('div');
if (model.get('debug')) {
el.classList.add('debug-resizer');
}
_innerdiv.classList.add('resized');
_innerdiv.innerHTML = model.get('value');
el.appendChild(_innerdiv);
let rob = new ResizeObserver((entries) => {
const width = parseInt(el.style.width);
const height = parseInt(el.style.height);
model.set('width', width);
model.set('height', height);
model.save_changes();
});
rob.observe(el);
model.on('change:value', () => {
_innerdiv.innerHTML = model.get('value');
});
model.on('change:debug', () => {
if (model.get('debug')) {
el.classList.add('debug-resizer');
} else {
el.classList.remove('debug-resizer');
}
});
model.on('change:width', () => {
const width = model.get('width');
const height = model.get('height');
if (width > 0) {
el.style.width = `${width}px`;
}
if (height > 0) {
el.style.height = `${height}px`;
}
});
model.on('change:height', () => {
const width = model.get('width');
const height = model.get('height');
if (width > 0) {
el.style.width = `${width}px`;
}
if (height > 0) {
el.style.height = `${height}px`;
}
});
const direction = model.get('direction');
if (direction == 'both') {
el.classList.add('resizer');
}
if (direction == 'vertical') {
el.classList.add('vresizer');
}
if (direction == 'horizontal') {
el.classList.add('hresizer');
}
width = parseInt(el.style.width);
height = parseInt(el.style.height);
model.set('width', width);
model.set('height', height);
model.save_changes();
}

export default { render };
3 changes: 2 additions & 1 deletion pkg/man/AnyWidget.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 10 additions & 18 deletions pkg/man/ResizeableWidget.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 71faf22

Please sign in to comment.