Skip to content

Commit e7e55f4

Browse files
add experimental Vue 3 support #10
1 parent c8dec5a commit e7e55f4

24 files changed

+16389
-9
lines changed

CRAN-RELEASE

Lines changed: 0 additions & 2 deletions
This file was deleted.

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Package: vueR
22
Type: Package
33
Title: 'Vuejs' Helpers and 'Htmlwidget'
4-
Version: 0.5.3
5-
Date: 2021-11-25
4+
Version: 0.6
5+
Date: 2022-01-03
66
Authors@R: c(
77
person(
88
"Evan","You"

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Generated by roxygen2: do not edit by hand
22

33
export(html_dependency_vue)
4+
export(html_dependency_vue3)
45
export(renderVue)
6+
export(renderVue3)
57
export(vue)
8+
export(vue3)
9+
export(vue3Output)
610
export(vueOutput)
711
import(htmlwidgets)
812
importFrom(htmltools,htmlDependency)

R/dependencies.R

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#'
88
#' @return \code{\link[htmltools]{htmlDependency}}
99
#' @importFrom htmltools htmlDependency
10+
#' @family dependencies
1011
#' @export
1112
#'
1213
#' @examples
@@ -53,3 +54,64 @@ html_dependency_vue <- function(offline=TRUE, minified=TRUE){
5354

5455
hd
5556
}
57+
58+
59+
#' Dependencies for 'Vue3'
60+
#'
61+
#' @param offline \code{logical} to use local file dependencies. If \code{FALSE},
62+
#' then the dependencies use cdn as its \code{src}.
63+
#' @param minified \code{logical} to use minified (production) version. Use
64+
#' \code{minified = FALSE} for debugging or working with Vue devtools.
65+
#'
66+
#' @return \code{\link[htmltools]{htmlDependency}}
67+
#' @importFrom htmltools htmlDependency
68+
#' @family dependencies
69+
#' @export
70+
#'
71+
#' @examples
72+
#' if(interactive()){
73+
#'
74+
#' library(vueR)
75+
#' library(htmltools)
76+
#'
77+
#' browsable(
78+
#' tagList(
79+
#' tags$div(id="app","{{message}}"),
80+
#' tags$script(
81+
#' "
82+
#' var app = {
83+
#' data: function() {
84+
#' return {
85+
#' message: 'Hello Vue!'
86+
#' }
87+
#' }
88+
#' };
89+
#'
90+
#' Vue.createApp(app).mount('#app');
91+
#' "
92+
#' ),
93+
#' html_dependency_vue3()
94+
#' )
95+
#' )
96+
#' }
97+
html_dependency_vue3 <- function(offline=TRUE, minified=TRUE){
98+
hd <- htmltools::htmlDependency(
99+
name = "vue",
100+
version = vue3_version(),
101+
src = system.file("www/vue3/dist",package="vueR"),
102+
script = "vue.global.prod.js"
103+
)
104+
105+
if(!minified) {
106+
hd$script <- "vue.global.js"
107+
}
108+
109+
if(!offline) {
110+
hd$src <- list(href=sprintf(
111+
"https://unpkg.com/vue@%s/dist/",
112+
vue3_version()
113+
))
114+
}
115+
116+
hd
117+
}

R/meta.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
#'@keywords internal
22
vue_version <- function(){'2.6.14'}
3+
4+
#'@keywords internal
5+
vue3_version <- function(){'3.2.26'}

R/vue.R

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#'
1616
#' @import htmlwidgets
1717
#'
18+
#' @family htmlwidget
1819
#' @export
1920
#' @example ./inst/examples/vue_widget_examples.R
2021
#' @return vue htmlwidget
@@ -45,6 +46,56 @@ vue <- function(
4546
hw
4647
}
4748

49+
#' 'Vue.js 3' 'htmlwidget'
50+
#'
51+
#' Use 'Vue.js 3' with the convenience and flexibility of 'htmlwidgets'.
52+
#' \code{vue3} is a little different from other 'htmlwidgets' though
53+
#' since it requires specification of the HTML tags/elements separately.
54+
#'
55+
#' @param app \code{list} with \code{el} and \code{data} and other pieces
56+
#' of a 'Vue.js 3' app
57+
#' @param width,height any valid \code{CSS} size unit, but in reality
58+
#' this will not currently have any impact
59+
#' @param elementId \code{character} id of the htmlwidget container
60+
#' element
61+
#' @param minified \code{logical} to indicate minified (\code{minified=TRUE}) or
62+
#' non-minified (\code{minified=FALSE}) Vue.js
63+
#'
64+
#' @import htmlwidgets
65+
#'
66+
#' @family htmlwidget
67+
#' @export
68+
#' @example ./inst/examples/vue3_widget_examples.R
69+
#' @return vue htmlwidget
70+
71+
vue3 <- function(
72+
app = list(),
73+
width = NULL, height = NULL, elementId = NULL,
74+
minified = TRUE
75+
) {
76+
77+
# forward options using x
78+
x = app
79+
80+
# will try to convert data that is not a function in widget JS
81+
82+
# create widget
83+
hw <- htmlwidgets::createWidget(
84+
name = 'vue3',
85+
x,
86+
width = width,
87+
height = height,
88+
package = 'vueR',
89+
elementId = elementId
90+
)
91+
92+
hw$dependencies <- list(
93+
html_dependency_vue3(offline=TRUE, minified=minified)
94+
)
95+
96+
hw
97+
}
98+
4899
#' Shiny bindings for vue
49100
#'
50101
#' Output and render functions for using vue within Shiny
@@ -62,6 +113,7 @@ vue <- function(
62113
#' @name vue-shiny
63114
#'
64115
#' @export
116+
#' @example ./inst/examples/vue3_shiny_examples.R
65117
vueOutput <- function(outputId, width = '100%', height = '400px'){
66118
htmlwidgets::shinyWidgetOutput(outputId, 'vue', width, height, package = 'vueR')
67119
}
@@ -72,3 +124,32 @@ renderVue <- function(expr, env = parent.frame(), quoted = FALSE) {
72124
if (!quoted) { expr <- substitute(expr) } # force quoted
73125
htmlwidgets::shinyRenderWidget(expr, vueOutput, env, quoted = TRUE)
74126
}
127+
128+
129+
#' Shiny bindings for 'vue 3'
130+
#'
131+
#' Output and render functions for using 'vue 3' within Shiny
132+
#' applications and interactive Rmd documents.
133+
#'
134+
#' @param outputId output variable to read from
135+
#' @param width,height Must be a valid CSS unit (like \code{'100\%'},
136+
#' \code{'400px'}, \code{'auto'}) or a number, which will be coerced to a
137+
#' string and have \code{'px'} appended.
138+
#' @param expr An expression that generates a vue
139+
#' @param env The environment in which to evaluate \code{expr}.
140+
#' @param quoted Is \code{expr} a quoted expression (with \code{quote()})? This
141+
#' is useful if you want to save an expression in a variable.
142+
#'
143+
#' @name vue-shiny
144+
#'
145+
#' @export
146+
vue3Output <- function(outputId, width = '100%', height = '400px'){
147+
htmlwidgets::shinyWidgetOutput(outputId, 'vue3', width, height, package = 'vueR')
148+
}
149+
150+
#' @rdname vue-shiny
151+
#' @export
152+
renderVue3 <- function(expr, env = parent.frame(), quoted = FALSE) {
153+
if (!quoted) { expr <- substitute(expr) } # force quoted
154+
htmlwidgets::shinyRenderWidget(expr, vueOutput, env, quoted = TRUE)
155+
}

build/getvue.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ download.file(
3131
# for use when creating dependencies
3232
cat(
3333
sprintf(
34-
"#'@keywords internal\nvue_version <- function(){'%s'}\n",
35-
get_vue_latest()
34+
"#'@keywords internal\nvue_version <- function(){'%s'}\n#'@keywords internal\nvue3_version <- function(){'%s'}\n",
35+
get_vue_latest(),
36+
"3.2.26" #hard coded for now
3637
),
3738
file = "./R/meta.R"
3839
)

cran-comments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Comments
22

3-
Second submission resolving license issue...
3+
Second submission resolving license issue by using CRAN template.
44

55
```
66
Thanks, we see:

inst/examples/vue3_shiny_examples.R

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
if(interactive()) {
2+
3+
library(shiny)
4+
library(vueR)
5+
6+
ui <- tagList(
7+
tags$div(id="app-3",
8+
tags$p("v-if"="seen", "Now you see me")
9+
),
10+
vue3Output('vue1')
11+
)
12+
13+
server <- function(input, output, session) {
14+
output$vue1 <- renderVue3({
15+
vue3(
16+
list(
17+
el = '#app-3',
18+
data = list(seen = TRUE),
19+
mounted = htmlwidgets::JS("function() {var that = this; setInterval(function(){that.seen=!that.seen},1000);}"),
20+
watch = list(
21+
seen = htmlwidgets::JS("function() {Shiny.setInputValue('seen',this.seen)}")
22+
)
23+
)
24+
)
25+
})
26+
27+
# show that Shiny input value is being updated
28+
observeEvent(input$seen, {print(input$seen)})
29+
}
30+
31+
shinyApp(ui, server)
32+
33+
}

inst/examples/vue3_widget_examples.R

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
if(interactive()) {
2+
3+
library(vueR)
4+
library(htmltools)
5+
6+
# recreate Hello Vue! example
7+
browsable(
8+
tagList(
9+
tags$div(id="app", "{{message}}"),
10+
vue3(
11+
list(
12+
el = "#app",
13+
# vue 3 is more burdensome but robust requiring data as function
14+
# if data is not a function then widget will auto-convert
15+
data = list(message = "Hello Vue3!")
16+
# data = htmlwidgets::JS("
17+
# function() {return {message: 'Hello Vue3!'}}
18+
# ")
19+
)
20+
)
21+
)
22+
)
23+
24+
# app2 from Vue.js introduction
25+
browsable(
26+
tagList(
27+
tags$div(id="app-2",
28+
tags$span(
29+
"v-bind:title" = "message",
30+
"Hover your mouse over me for a few seconds to see my dynamically bound title!"
31+
)
32+
),
33+
vue3(
34+
list(
35+
el = "#app-2",
36+
# vue 3 is more burdensome but robust requiring data as function
37+
# if data is not a function then widget will auto-convert
38+
data = htmlwidgets::JS("
39+
function() {
40+
return {message: 'You loaded this page on ' + new Date()}
41+
}
42+
")
43+
)
44+
)
45+
)
46+
)
47+
48+
# app3 from Vue.js introduction
49+
# with a setInterval to toggle seen true and false
50+
browsable(
51+
tagList(
52+
tags$div(id="app-3",
53+
tags$p("v-if"="seen", "Now you see me")
54+
),
55+
vue3(
56+
list(
57+
el = '#app-3',
58+
data = list(seen = TRUE),
59+
# data = htmlwidgets::JS("function() {return {seen: true}}"),
60+
mounted = htmlwidgets::JS("function() {var that = this; setInterval(function(){that.seen=!that.seen},1000);}")
61+
)
62+
)
63+
)
64+
)
65+
66+
}

inst/examples/vue_widget_examples.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if(interactive()) {
77
browsable(
88
tagList(
99
tags$div(id="app", "{{message}}"),
10-
vue(
10+
vue3(
1111
list(
1212
el = "#app",
1313
data = list(

inst/htmlwidgets/vue3.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
HTMLWidgets.widget({
2+
3+
name: 'vue3',
4+
5+
type: 'output',
6+
7+
factory: function(el, width, height) {
8+
9+
var instance = {};
10+
11+
return {
12+
13+
renderValue: function(x) {
14+
// if x.el is specified then use it and
15+
// hide our htmlwidget container
16+
if(x.el) {
17+
el.style.display = "none";
18+
} else {
19+
// x.el not specified so use our htmlwidget el
20+
// container for our Vue app but this will
21+
// will probably not work as expected
22+
// since the tag is just a div
23+
x.el = "#" + el.id;
24+
}
25+
26+
// Vue 3 requires data to be a function
27+
// so if not a function then we will auto-convert to a function
28+
if(typeof(x.data) !== 'function') {
29+
var dat = Object.assign({}, x.data);
30+
x.data = function() {return dat};
31+
}
32+
33+
this.instance = Vue.createApp(x).mount(x.el);
34+
35+
},
36+
37+
resize: function(width, height) {
38+
39+
// for now ignore resize hopefully without
40+
// much consequence
41+
42+
},
43+
44+
instance: instance
45+
46+
};
47+
}
48+
});

inst/htmlwidgets/vue3.yaml

Whitespace-only changes.

0 commit comments

Comments
 (0)