| @@ -19,4 +19,4 @@ ByteCompile: true | |||
| Encoding: UTF-8 | |||
| LazyData: true | |||
| Roxygen: list(markdown = TRUE) | |||
| RoxygenNote: 7.0.2 | |||
| RoxygenNote: 7.1.1 | |||
| @@ -1,6 +1,7 @@ | |||
| # Generated by roxygen2: do not edit by hand | |||
| export(emoji2html) | |||
| export(emoji_picker) | |||
| export(ermoji_gadget) | |||
| export(ermoji_shiny) | |||
| import(miniUI) | |||
| @@ -0,0 +1,133 @@ | |||
| #' Emoji Picker | |||
| #' | |||
| #' An emoji picker gadget that lets you search and find emoji and insert either | |||
| #' the unicode, HTML, or `emo::ji()` versions of the emoji. Built using the | |||
| #' [Emoji Button](https://emoji-button.js.org/) JavaScript library. | |||
| #' | |||
| #' @return A list with the `emoji`, the emoji `name` and the `html` entity. | |||
| #' | |||
| #' @param gadget If `TRUE` the app is run as a gadget, otherwise it's run as a | |||
| #' standalone app. | |||
| #' | |||
| #' @export | |||
| emoji_picker <- function(gadget = TRUE) { | |||
| shiny::addResourcePath("eb", system.file("picker", package = "ermoji")) | |||
| if (gadget) { | |||
| shiny::runGadget( | |||
| emoji_picker_ui(), | |||
| emoji_picker_server(quick_add = TRUE), | |||
| viewer = shiny::dialogViewer("ermoji", width = 375, height = 463) | |||
| ) | |||
| } else { | |||
| shiny::shinyApp(emoji_picker_ui(), emoji_picker_server(quick_add = FALSE)) | |||
| } | |||
| } | |||
| emoji_picker_ui <- function() { | |||
| shiny::fluidPage( | |||
| id = "picker-gadget", | |||
| class = paste( | |||
| if (is_rstudio_dark()) "dark-theme", | |||
| get_picker_type() | |||
| ), | |||
| shiny::div(id = "emoji-picker", style = "width: 100%; min-height: 425; position: relative;"), | |||
| rstudio_style(), | |||
| shiny::tags$script(src = "eb/emoji-picker.js", type = "module"), | |||
| shiny::tags$head( | |||
| shiny::tags$script(src = 'eb/he.js'), | |||
| shiny::tags$style('.emoji-picker__plugin-container { justify-content: space-between; }') | |||
| ) | |||
| ) | |||
| } | |||
| is_rstudio_dark <- function() { | |||
| if (rstudioapi::hasFun("getThemeInfo")) { | |||
| rstudioapi::getThemeInfo()$dark | |||
| } else { | |||
| FALSE | |||
| } | |||
| } | |||
| rstudio_style <- function() { | |||
| if (!rstudioapi::hasFun("getThemeInfo")) { | |||
| return(NULL) | |||
| } | |||
| theme <- rstudioapi::getThemeInfo() | |||
| shiny::tags$style(shiny::HTML(paste0( | |||
| ".emoji-picker {\n", | |||
| " --background-color: ", theme$background, ";\n", | |||
| " --text-color: ", theme$foreground, ";\n", | |||
| "}\n", | |||
| "body { background-color: ", theme$background, "; color: ", theme$foreground, ";}\n", | |||
| "#picker_type {\n", | |||
| " display: flex;\n", | |||
| " justify-content: space-between;\n", | |||
| " width: 100%;\n", | |||
| "}" | |||
| ))) | |||
| } | |||
| set_picker_type <- function(style = c("unicode", "html", "emo_ji")) { | |||
| if (!rstudioapi::hasFun("setPersistentValue")) { | |||
| return() | |||
| } | |||
| style <- match.arg(style) | |||
| rstudioapi::setPersistentValue("ermoji.picker_type", style) | |||
| invisible(style) | |||
| } | |||
| get_picker_type <- function() { | |||
| if (!rstudioapi::hasFun("setPersistentValue")) { | |||
| return("unicode") | |||
| } | |||
| style <- rstudioapi::getPersistentValue("ermoji.picker_type") | |||
| if (is.null(style)) { | |||
| return("unicode") | |||
| } | |||
| if (!style %in% c("unicode", "html", "emo_ji")) { | |||
| return("unicode") | |||
| } | |||
| style | |||
| } | |||
| emoji_picker_server <- function(quick_add = TRUE) { | |||
| function(input, output, session) { | |||
| shiny::observeEvent(input$close, { | |||
| shiny::stopApp(invisible(input$emoji)) | |||
| }) | |||
| shiny::observe({ | |||
| session$sendCustomMessage('update_picker_type', get_picker_type()) | |||
| }) | |||
| shiny::observeEvent(input$picker_type, { | |||
| set_picker_type(input$picker_type) | |||
| }) | |||
| shiny::observeEvent(input$emoji, { | |||
| emoji <- switch( | |||
| input$picker_type, | |||
| "unicode" = input$emoji$emoji, | |||
| "html" = input$emoji$html, | |||
| "emo_ji" = { | |||
| tryCatch({ | |||
| emo::ji(input$emoji$name) | |||
| paste0('emo::ji("', input$emoji$name, '")') | |||
| }, | |||
| error = function(e) { | |||
| message("{emo} doesn't know the emoji ", shQuote(input$emoji$name)) | |||
| NULL | |||
| } | |||
| ) | |||
| } | |||
| ) | |||
| if (!is.null(emoji)) { | |||
| rstudioapi::insertText(emoji, id = rstudioapi::documentId()) | |||
| if (isTRUE(quick_add)) { | |||
| shiny::stopApp(invisible(input$emoji)) | |||
| } | |||
| } | |||
| }) | |||
| } | |||
| } | |||
| @@ -0,0 +1,104 @@ | |||
| import { EmojiButton } from './emoji-button.js' // @joeattardi/emoji-button | |||
| const pickerTypePlugin = { | |||
| render(picker) { | |||
| const choices = [ | |||
| {value: 'unicode', label: '<span>Unicode</span>'}, | |||
| {value: 'html', label: '<span>HTML</span>'}, | |||
| {value: 'emo_ji', label: '<span style="font-family: monospace;">emo::ji</span>'} | |||
| ] | |||
| function htmlToElems(html) { | |||
| let temp = document.createElement('template'); | |||
| temp.innerHTML = html; | |||
| return temp.content.childNodes; | |||
| } | |||
| function makeRadioElement({value, label}) { | |||
| const div = document.createElement('div') | |||
| const divLabel = document.createElement('label') | |||
| divLabel.classList = 'radio-inline' | |||
| const input = document.createElement('input') | |||
| input.type = 'radio' | |||
| input.name = 'picker_type' | |||
| input.value = value | |||
| if (document.getElementById('picker-gadget').matches('.' + value)) { | |||
| input.checked = 'checked' | |||
| } | |||
| divLabel.appendChild(input) | |||
| divLabel.appendChild(htmlToElems(label)[0]) | |||
| div.appendChild(divLabel) | |||
| return div | |||
| } | |||
| const div = document.createElement('div') | |||
| div.id = 'picker_type' | |||
| const label = document.createElement('label') | |||
| label.classList = 'control-label' | |||
| label['for'] = 'picker_type' | |||
| label.innerText = 'Insert as...' | |||
| div.appendChild(label) | |||
| choices.map(makeRadioElement).forEach(el => div.appendChild(el)) | |||
| return div; | |||
| } | |||
| } | |||
| const picker = new EmojiButton({ | |||
| position: { | |||
| top: '0', | |||
| right: '0', | |||
| bottom: '0', | |||
| left: '0' | |||
| }, | |||
| autoHide: false, | |||
| theme: document.getElementById('picker-gadget').matches('.dark-theme') ? 'dark' : 'light', | |||
| // plugins: [closePlugin, insertEmoji] | |||
| plugins: [pickerTypePlugin] | |||
| }) | |||
| picker.on('emoji', function(selection) { | |||
| selection.html = he.encode(selection.emoji) | |||
| Shiny.setInputValue('emoji', selection, { priority: 'event' }); | |||
| }); | |||
| picker.togglePicker(document.getElementById('emoji-picker')) | |||
| document.querySelector('.emoji-picker__wrapper').addEventListener('keydown', function(ev) { | |||
| if (ev.keyCode === 27) { | |||
| ev.stopPropagation() | |||
| Shiny.setInputValue('close', Date.now()) | |||
| } | |||
| }) | |||
| // Communicate choices of emoji type to insert ... | |||
| const pickerInput = document.getElementById('picker_type') | |||
| function reportPickerType() { | |||
| const choices = [...pickerInput.querySelectorAll('input')] | |||
| const value = choices.filter(item => item.checked).map(item => item.value) | |||
| Shiny.setInputValue('picker_type', value[0]) | |||
| } | |||
| function updatePickerType(value) { | |||
| const choices = [...pickerInput.querySelectorAll('input')] | |||
| choices.forEach(function(choice) { | |||
| if (choice.value === value) { | |||
| choice.checked = 'checked' | |||
| Shiny.setInputValue('picker_type', value) | |||
| } else { | |||
| choice.removeAttribute('checked') | |||
| } | |||
| }) | |||
| } | |||
| pickerInput.addEventListener('change', reportPickerType) | |||
| $().on('shiny:sessioninitialized', reportPickerType) | |||
| Shiny.addCustomMessageHandler('update_picker_type', updatePickerType) | |||
| @@ -2,3 +2,9 @@ Name: Search and Copy Emoji | |||
| Description: Search for emoji and copy name, unicode, or gliph. | |||
| Binding: ermoji_gadget | |||
| Interactive: true | |||
| Name: Pick and Insert Emoji | |||
| Description: Insert emoji from picker | |||
| Binding: emoji_picker | |||
| Interactive: true | |||
| @@ -0,0 +1,20 @@ | |||
| % Generated by roxygen2: do not edit by hand | |||
| % Please edit documentation in R/emoji_picker.R | |||
| \name{emoji_picker} | |||
| \alias{emoji_picker} | |||
| \title{Emoji Picker} | |||
| \usage{ | |||
| emoji_picker(gadget = TRUE) | |||
| } | |||
| \arguments{ | |||
| \item{gadget}{If \code{TRUE} the app is run as a gadget, otherwise it's run as a | |||
| standalone app.} | |||
| } | |||
| \value{ | |||
| A list with the \code{emoji}, the emoji \code{name} and the \code{html} entity. | |||
| } | |||
| \description{ | |||
| An emoji picker gadget that lets you search and find emoji and insert either | |||
| the unicode, HTML, or \code{emo::ji()} versions of the emoji. Built using the | |||
| \href{https://emoji-button.js.org/}{Emoji Button} JavaScript library. | |||
| } | |||