🤷‍♂️ RStudio Addin to Search and Copy Emoji
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

220 lines
7.3KB

  1. #' The ermoji emoji gadget
  2. #'
  3. #' Opens a miniUI based Shiny gadget in the RStudio Viewer pane with a
  4. #' searchable table of emoji. Select a row and click the copy desired button.
  5. #'
  6. #' @param clipout Should the gadget attempt to write to the clipboard?
  7. #' @param ... Ignored at this time
  8. #' @name ermoji
  9. #' @return nothing
  10. #' @export
  11. ermoji_gadget <- function(clipout = clipr::clipr_available(), ...) {
  12. shiny::runGadget(ermoji_ui, ermoji_server(clipout, ...), viewer = shiny::paneViewer(500), stopOnCancel = FALSE)
  13. }
  14. #' @rdname ermoji
  15. #' @export
  16. ermoji_shiny <- function(clipout = clipr::clipr_available(), ...) {
  17. shiny::shinyApp(ui = ermoji_ui, server = ermoji_server(clipout, ...))
  18. }
  19. ermoji_ui <- miniUI::miniPage(
  20. title = "ermoji",
  21. shiny::tags$head(
  22. shiny::tags$style(
  23. shiny::HTML("
  24. .dropdown-item {
  25. display: block;
  26. width: 100%;
  27. padding: .25rem 1.5rem;
  28. clear: both;
  29. font-weight: 400;
  30. color: #212529;
  31. text-align: inherit;
  32. white-space: nowrap;
  33. background-color: transparent;
  34. border: 0;
  35. }
  36. .dropdown-menu {
  37. color: #212529;
  38. text-align: left;
  39. list-style: none;
  40. }
  41. @media (min-height: 600px) {
  42. td.big { font-size: 2em; }
  43. }
  44. @media (max-height: 599px) {
  45. td.big { font-size: 1.5em; }
  46. }
  47. table.dataTable tbody td {
  48. vertical-align: middle;
  49. }
  50. ")
  51. )
  52. ),
  53. miniUI::gadgetTitleBar("ermoji"),
  54. miniUI::miniContentPanel(
  55. padding = 10,
  56. DT::dataTableOutput('emojis', height = "100%", width = "98%")
  57. ),
  58. miniUI::miniButtonBlock(
  59. shiny::actionButton("copy_name", "Copy :emoji_name:", class = "btn-success"),
  60. shiny::tags$div(class = "btn-group dropup", style = "width: 33%",
  61. shiny::tags$button(class = "btn btn-warning dropdown-toggle", href = "#",
  62. role = "button", id = "dropdownMenuLink", style = "width: 100%",
  63. "data-toggle" = "dropdown", "aria-haspopup" = "true",
  64. "aria-expanded" = "false",
  65. "Copy Unicode"),
  66. shiny::tags$div(class = "dropdown-menu", style = "width: 100%",
  67. "aria-labelledby"="dropdownMenuLink",
  68. shiny::actionLink("copy_utf", "Copy unicode", class = "dropdown-item"),
  69. shiny::actionLink("copy_html", "Copy HTML", class = "dropdown-item")
  70. )
  71. ),
  72. shiny::actionButton("copy_gliph", "Copy Emoji", class = "btn-primary")
  73. )
  74. )
  75. ermoji_server <- function(clipout = clipr::clipr_available()) {
  76. function(input, output, session) {
  77. output$emojis <- DT::renderDataTable({
  78. emojis <- emo::jis
  79. emojis <- emojis[, c('emoji', 'name', "group", "keywords", "aliases")]
  80. emojis$group <- factor(emojis$group)
  81. emojis$keywords <- vapply(emojis$keywords, function(x) paste(x, collapse = ", "), character(1))
  82. emojis$aliases <- vapply(emojis$aliases, function(x) paste(x, collapse = ", "), character(1))
  83. DT::datatable(
  84. emojis,
  85. rownames = FALSE,
  86. colnames = c("Emoji", "Name", "Group", "Keywords", "Aliases"),
  87. filter = "top",
  88. selection = "single",
  89. fillContainer = TRUE,
  90. style = 'bootstrap',
  91. class = 'compact stripe hover',
  92. extensions = c("Scroller"),
  93. options = list(
  94. dom = "<'row'<'col-sm-6 col-sm-offset-2 text-center'f>>tir",
  95. searchHighlight = TRUE,
  96. search = list(regex = TRUE, caseInsensitive = FALSE),
  97. autoWidth = FALSE,
  98. columnDefs = list(
  99. list(
  100. className = "dt-center big", searchable = FALSE, width = "10%", targets = 0
  101. ),
  102. list(width = "20%", targets = c(1, 3, 4)),
  103. list(width = "10%", targets = 2)
  104. ),
  105. deferRender = TRUE,
  106. scrollY = 500,
  107. scroller = TRUE
  108. )
  109. )
  110. })
  111. this_emoji <- shiny::reactive({
  112. shiny::req(input$emojis_rows_selected)
  113. as.list(emo::jis[input$emojis_rows_selected, ])
  114. })
  115. this_emoji_name <- shiny::reactive({
  116. # name <- this_emoji()$name
  117. name <- this_emoji()$aliases[[1]][1]
  118. paste0(":", gsub(" ", "_", name), ":")
  119. })
  120. this_emoji_uni <- shiny::reactive({
  121. uni <- paste0("\\U", this_emoji()$runes)
  122. gsub(" ", "\\\\U", uni)
  123. })
  124. this_emoji_html <- shiny::reactive({
  125. rune2html(this_emoji()$runes)
  126. })
  127. truncate <- function(x, n = 10) {
  128. if (nchar(x) > n) {
  129. paste0(strtrim(x, n), "...")
  130. } else x
  131. }
  132. shiny::observeEvent(input$emojis_rows_selected, {
  133. if (!shiny::isTruthy(input$emojis_rows_selected)) {
  134. shiny::updateActionButton(session, "copy_name", "Copy :emoji_name:")
  135. shiny::updateActionButton(session, "copy_utf", "Copy Unicode")
  136. shiny::updateActionButton(session, "copy_html", "Copy HTML")
  137. shiny::updateActionButton(session, "copy_gliph", "Copy Emoji")
  138. } else {
  139. shiny::updateActionButton(session, "copy_name", paste0("Copy <code>", this_emoji_name(), "</code>"))
  140. shiny::updateActionButton(session, "copy_utf", paste("Copy Unicode: <code>", truncate(this_emoji_uni()), "</code>"))
  141. shiny::updateActionButton(session, "copy_html", paste("Copy HTML: <code>", escape_html(truncate(this_emoji_html())), "</code>"))
  142. shiny::updateActionButton(session, "copy_gliph", paste("Copy", this_emoji()$emoji))
  143. }
  144. })
  145. copy_modal <- function(text) {
  146. shiny::showModal(
  147. shiny::modalDialog(
  148. title = "Select and Copy",
  149. shiny::tags$p("I don't have access to your clipboard. Select the text and", shiny::tags$kbd("Ctrl/Cmd"), "+", shiny::tags$kbd("c"), "to copy."),
  150. shiny::tags$pre(text),
  151. easyClose = TRUE
  152. )
  153. )
  154. }
  155. shiny::observeEvent(input$copy_name, {
  156. if (clipout) clipr::write_clip(this_emoji_name()) else copy_modal(this_emoji_name())
  157. })
  158. shiny::observeEvent(input$copy_utf, {
  159. if (clipout) clipr::write_clip(this_emoji_uni()) else copy_modal(this_emoji_uni())
  160. })
  161. shiny::observeEvent(input$copy_html, {
  162. if (clipout) clipr::write_clip(this_emoji_html()) else copy_modal(this_emoji_html())
  163. })
  164. shiny::observeEvent(input$copy_gliph, {
  165. if (clipout) clipr::write_clip(this_emoji()$emoji) else copy_modal(this_emoji()$emoji)
  166. })
  167. shiny::observeEvent(input$done, {
  168. shiny::stopApp(invisible())
  169. })
  170. shiny::observeEvent(input$cancel, {
  171. shiny::stopApp(invisible())
  172. })
  173. }
  174. }
  175. escape_html <- function(x) {
  176. x = gsub('&', '&amp;', x)
  177. x = gsub('<', '&lt;', x)
  178. x = gsub('>', '&gt;', x)
  179. x = gsub('"', '&quot;', x)
  180. x
  181. }
  182. #' Convert Emoji to HTML
  183. #'
  184. #' Uses [emo::ji()] to look up emoji, but return the escaped HTML.
  185. #'
  186. #' @param x A search term passed to [emo::ji()] or the result of [emo::ji()].
  187. #' @param copy Should the result be copied to the clipboard?
  188. #' @export
  189. emoji2html <- function(x, copy = clipr::clipr_available()) {
  190. emojis <- emo::jis$runes
  191. names(emojis) <- emo::jis$emoji
  192. if (!inherits(x, "emoji")) {
  193. x <- emo::ji(x)
  194. }
  195. emoji <- emojis[x]
  196. emoji_html <- rune2html(unname(emoji))
  197. if (copy) {
  198. clipr::write_clip(emoji_html)
  199. message(emoji_html, " copied to clipboard")
  200. return(invisible(emoji_html))
  201. }
  202. emoji_html
  203. }
  204. rune2html <- function(runes) {
  205. gsub("([0-9A-F]{4,8}) ?", "&#x\\1;", runes)
  206. }