🤷‍♂️ RStudio Addin to Search and Copy Emoji
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
4.9KB

  1. #' Emoji Picker
  2. #'
  3. #' An emoji picker gadget that lets you search and find emoji and insert either
  4. #' the unicode, HTML, or `emo::ji()` versions of the emoji. Built using the
  5. #' [Emoji Button](https://emoji-button.js.org/) JavaScript library.
  6. #'
  7. #' @return A list with the `emoji`, the emoji `name` and the `html` entity.
  8. #'
  9. #' @param gadget If `TRUE` the app is run as a gadget, otherwise it's run as a
  10. #' standalone app.
  11. #'
  12. #' @export
  13. emoji_picker <- function(gadget = TRUE) {
  14. shiny::addResourcePath("eb", system.file("picker", package = "ermoji"))
  15. if (gadget) {
  16. shiny::runGadget(
  17. emoji_picker_ui(),
  18. emoji_picker_server(quick_add = TRUE),
  19. viewer = shiny::dialogViewer("ermoji", width = 375, height = 463)
  20. )
  21. } else {
  22. shiny::shinyApp(emoji_picker_ui(), emoji_picker_server(quick_add = FALSE))
  23. }
  24. }
  25. emoji_picker_ui <- function() {
  26. shiny::fluidPage(
  27. id = "picker-gadget",
  28. class = paste(
  29. if (is_rstudio_dark()) "dark-theme",
  30. get_picker_type()
  31. ),
  32. shiny::div(id = "emoji-picker", style = "width: 100%; min-height: 425; position: relative;"),
  33. shiny::HTML('<div id="alert_bad_emo_ji" class="alert alert-danger" role="alert"><strong style="font-family: monospace">emo</strong> doesn\'t know the emoji <strong id="bad_emo_ji_name"></strong></div>'),
  34. rstudio_style(),
  35. shiny::tags$script(src = "eb/emoji-picker.js", type = "module"),
  36. shiny::tags$head(
  37. shiny::tags$script(src = 'eb/he.js'),
  38. shiny::tags$style('
  39. .emoji-picker__plugin-container { justify-content: space-between; }
  40. .emoji-picker { border-radius: 0 !important; }
  41. #alert_bad_emo_ji {
  42. position: fixed;
  43. top: 0;
  44. left: 0;
  45. right: 0;
  46. z-index: 9999;
  47. border-radius: 0;
  48. transform: translateY(-100px);
  49. transition: transform 0.5s ease-in;
  50. text-align: center;
  51. }
  52. #alert_bad_emo_ji.show-bad-emo-ji {
  53. transform: translateY(0);
  54. }
  55. #bad_emo_ji_name {
  56. white-space: nowrap;
  57. }
  58. '
  59. )
  60. )
  61. )
  62. }
  63. is_rstudio_dark <- function() {
  64. if (rstudioapi::hasFun("getThemeInfo")) {
  65. rstudioapi::getThemeInfo()$dark
  66. } else {
  67. FALSE
  68. }
  69. }
  70. rstudio_style <- function() {
  71. if (!rstudioapi::hasFun("getThemeInfo")) {
  72. return(NULL)
  73. }
  74. theme <- rstudioapi::getThemeInfo()
  75. shiny::tags$style(shiny::HTML(paste0(
  76. ".emoji-picker {\n",
  77. if (theme$dark) " --dark-background-color: " else " --background-color: ", theme$background, ";\n",
  78. if (theme$dark) " --dark-text-color: " else " --text-color: ", theme$foreground, ";\n",
  79. "}\n",
  80. "body { background-color: ", theme$background, "; color: ", theme$foreground, ";}\n",
  81. "#picker_type {\n",
  82. " display: flex;\n",
  83. " justify-content: space-between;\n",
  84. " width: 100%;\n",
  85. "}"
  86. )))
  87. }
  88. set_picker_type <- function(style = c("unicode", "html", "emo_ji")) {
  89. if (!rstudioapi::hasFun("setPersistentValue")) {
  90. return()
  91. }
  92. style <- match.arg(style)
  93. rstudioapi::setPersistentValue("ermoji.picker_type", style)
  94. invisible(style)
  95. }
  96. get_picker_type <- function() {
  97. if (!rstudioapi::hasFun("setPersistentValue")) {
  98. return("unicode")
  99. }
  100. style <- rstudioapi::getPersistentValue("ermoji.picker_type")
  101. if (is.null(style)) {
  102. return("unicode")
  103. }
  104. if (!style %in% c("unicode", "html", "emo_ji")) {
  105. return("unicode")
  106. }
  107. style
  108. }
  109. emoji_picker_server <- function(quick_add = TRUE, context = NULL) {
  110. if (is.null(context)) {
  111. context <- rstudioapi::getActiveDocumentContext()
  112. }
  113. initial_search <- if (nzchar(context$selection[[1]]$text)) {
  114. context$selection[[1]]$text
  115. }
  116. function(input, output, session) {
  117. shiny::observeEvent(input$close, {
  118. shiny::stopApp(invisible(input$emoji))
  119. })
  120. shiny::observe({
  121. session$sendCustomMessage('update_picker_type', get_picker_type())
  122. session$sendCustomMessage("search_emoji", initial_search)
  123. })
  124. shiny::observeEvent(input$picker_type, {
  125. set_picker_type(input$picker_type)
  126. })
  127. shiny::observeEvent(input$emoji, {
  128. emoji <- switch(
  129. input$picker_type,
  130. "unicode" = input$emoji$emoji,
  131. "html" = input$emoji$html,
  132. "emo_ji" = try_emo_ji(input$emoji$name, session = session)
  133. )
  134. if (!is.null(emoji)) {
  135. rstudioapi::insertText(
  136. location = Map(function(x) x$range, context$selection),
  137. text = emoji,
  138. id = context$id
  139. )
  140. if (isTRUE(quick_add)) {
  141. shiny::stopApp(invisible(input$emoji))
  142. }
  143. }
  144. })
  145. }
  146. }
  147. try_emo_ji <- function(name, session = shiny::getDefaultReactiveDomain()) {
  148. emo_ji <- tryCatch({
  149. emo::ji(input$emoji$name)
  150. paste0('emo::ji("', input$emoji$name, '")')
  151. },
  152. error = function(e) {
  153. NULL
  154. }
  155. )
  156. if (!is.null(emo_ji)) return(emo_ji)
  157. name_no_space <- gsub(" ", "_", name)
  158. tryCatch({
  159. emo::ji(name_no_space)
  160. paste0('emo::ji("', name_no_space, '")')
  161. },
  162. error = function(e) {
  163. session$sendCustomMessage("bad_emo_ji", name)
  164. message("{emo} doesn't know the emoji ", shQuote(name))
  165. NULL
  166. }
  167. )
  168. }