Skip to content

Instantly share code, notes, and snippets.

@ikappaki
Created March 7, 2022 20:00
Show Gist options
  • Save ikappaki/a2ccf02d49724cd55e6d70a0df2ca7da to your computer and use it in GitHub Desktop.
Save ikappaki/a2ccf02d49724cd55e6d70a0df2ca7da to your computer and use it in GitHub Desktop.
Attempt to enhance clerk table-viewer from userland
(ns table-uland-cp-copy
"A first attempt of a userland hack to enhance the current
table-viewer with a copy icon on the top right corner, than when
clicked it will copy the table contents to the clipboard.
Part of a study suggested by @mkvlr to see how far one can go with
implementing a viewer in userland and the difficulties they could
face."
(:require [nextjournal.clerk :as c]
[nextjournal.clerk.viewer :as v]
[clojure.string :as str]))
(def copy-icon
"The copy icon."
[:svg#Layer_1 {:version "1.1" :xmlns "http://www.w3.org/2000/svg"
:xmlns:xlink "http://www.w3.org/1999/xlink"
:x "0px" :y "0px" :viewBox "0 0 115.77 122.88" :xml:space "preserve"}
[:style {:type "text/css"}
".st0{fill-rule:evenodd;clip-rule:evenodd;}"]
[:g [:path.st0 {:d "M89.62,13.96v7.73h12.19h0.01v0.02c3.85,0.01,7.34,1.57,9.86,4.1c2.5,2.51,4.06,5.98,4.07,9.82h0.02v0.02 v73.27v0.01h-0.02c-0.01,3.84-1.57,7.33-4.1,9.86c-2.51,2.5-5.98,4.06-9.82,4.07v0.02h-0.02h-61.7H40.1v-0.02 c-3.84-0.01-7.34-1.57-9.86-4.1c-2.5-2.51-4.06-5.98-4.07-9.82h-0.02v-0.02V92.51H13.96h-0.01v-0.02c-3.84-0.01-7.34-1.57-9.86-4.1 c-2.5-2.51-4.06-5.98-4.07-9.82H0v-0.02V13.96v-0.01h0.02c0.01-3.85,1.58-7.34,4.1-9.86c2.51-2.5,5.98-4.06,9.82-4.07V0h0.02h61.7 h0.01v0.02c3.85,0.01,7.34,1.57,9.86,4.1c2.5,2.51,4.06,5.98,4.07,9.82h0.02V13.96L89.62,13.96z M79.04,21.69v-7.73v-0.02h0.02 c0-0.91-0.39-1.75-1.01-2.37c-0.61-0.61-1.46-1-2.37-1v0.02h-0.01h-61.7h-0.02v-0.02c-0.91,0-1.75,0.39-2.37,1.01 c-0.61,0.61-1,1.46-1,2.37h0.02v0.01v64.59v0.02h-0.02c0,0.91,0.39,1.75,1.01,2.37c0.61,0.61,1.46,1,2.37,1v-0.02h0.01h12.19V35.65 v-0.01h0.02c0.01-3.85,1.58-7.34,4.1-9.86c2.51-2.5,5.98-4.06,9.82-4.07v-0.02h0.02H79.04L79.04,21.69z M105.18,108.92V35.65v-0.02 h0.02c0-0.91-0.39-1.75-1.01-2.37c-0.61-0.61-1.46-1-2.37-1v0.02h-0.01h-61.7h-0.02v-0.02c-0.91,0-1.75,0.39-2.37,1.01 c-0.61,0.61-1,1.46-1,2.37h0.02v0.01v73.27v0.02h-0.02c0,0.91,0.39,1.75,1.01,2.37c0.61,0.61,1.46,1,2.37,1v-0.02h0.01h61.7h0.02 v0.02c0.91,0,1.75-0.39,2.37-1.01c0.61-0.61,1-1.46,1-2.37h-0.02V108.92L105.18,108.92z"}]]])
(def table-clip-render-fn
"Wraps v/table-viewer into a react fragment that displays a copy icon
at the right top corner, that when clicked copies the table to the
system clipboard."
`(fn [data opts]
(let [table (v/table-viewer data opts)]
(update table :nextjournal/value
(fn [table]
(let [table-id (random-uuid)
;; The table doesn't come with an id, assign
;; a unique id.
table (assoc table 1 {:id table-id})]
[:<>
[:div;;.relative
{:style {:visibility :visible
:z-index 50
:position "relative"}}
[:div;;.top-0.right-0.h-6.w-6
{:style {:top 0
:right 0
:position "absolute"
:height "1.5rem"
:width "1.5rem"}
:on-click (fn [_e]
(let [range (js/document.createRange)
ct (js/document.getElementById table-id)]
(js/console.log ct)
(.selectNode range ct)
(-> (js/window.getSelection)
(.addRange range))
(js/document.execCommand "copy")
))}
~copy-icon]]
table]))))))
^::c/no-cache
(c/with-viewers
;; Replace render-fn of :table viewer.
[(-> (some #(do (println (:name %))
(when (= (:name %) :table) %)) (:root @v/!viewers))
(assoc :render-fn table-clip-render-fn))]
(v/with-viewer :table
;; a table with 21 rows and a single column with an increasing text
;; length.
(mapv #(hash-map :a (str/join (repeat (* 6 %) "x"))) (take 21 (range)))))
@ikappaki
Copy link
Author

ikappaki commented Mar 8, 2022

(ns table-uland-cp-copy
  "Functional changes:

  1. Use tailwind utilities to position and hide/show the copy icon on
  hover."
  (:require [nextjournal.clerk :as c]
            [nextjournal.clerk.viewer :as v]
            [clojure.string :as str]))

(def copy-icon
  [:svg {:version "1.1" :xmlns "http://www.w3.org/2000/svg"
         :xmlns:xlink "http://www.w3.org/1999/xlink"
         :x "0px" :y "0px" :viewBox "0 0 115.77 122.88"  :xml:space "preserve"}
   [:style {:type "text/css"}
    ".st0{fill-rule:evenodd;clip-rule:evenodd;}"]
   [:g [:path.st0 {:d "M89.62,13.96v7.73h12.19h0.01v0.02c3.85,0.01,7.34,1.57,9.86,4.1c2.5,2.51,4.06,5.98,4.07,9.82h0.02v0.02 v73.27v0.01h-0.02c-0.01,3.84-1.57,7.33-4.1,9.86c-2.51,2.5-5.98,4.06-9.82,4.07v0.02h-0.02h-61.7H40.1v-0.02 c-3.84-0.01-7.34-1.57-9.86-4.1c-2.5-2.51-4.06-5.98-4.07-9.82h-0.02v-0.02V92.51H13.96h-0.01v-0.02c-3.84-0.01-7.34-1.57-9.86-4.1 c-2.5-2.51-4.06-5.98-4.07-9.82H0v-0.02V13.96v-0.01h0.02c0.01-3.85,1.58-7.34,4.1-9.86c2.51-2.5,5.98-4.06,9.82-4.07V0h0.02h61.7 h0.01v0.02c3.85,0.01,7.34,1.57,9.86,4.1c2.5,2.51,4.06,5.98,4.07,9.82h0.02V13.96L89.62,13.96z M79.04,21.69v-7.73v-0.02h0.02 c0-0.91-0.39-1.75-1.01-2.37c-0.61-0.61-1.46-1-2.37-1v0.02h-0.01h-61.7h-0.02v-0.02c-0.91,0-1.75,0.39-2.37,1.01 c-0.61,0.61-1,1.46-1,2.37h0.02v0.01v64.59v0.02h-0.02c0,0.91,0.39,1.75,1.01,2.37c0.61,0.61,1.46,1,2.37,1v-0.02h0.01h12.19V35.65 v-0.01h0.02c0.01-3.85,1.58-7.34,4.1-9.86c2.51-2.5,5.98-4.06,9.82-4.07v-0.02h0.02H79.04L79.04,21.69z M105.18,108.92V35.65v-0.02 h0.02c0-0.91-0.39-1.75-1.01-2.37c-0.61-0.61-1.46-1-2.37-1v0.02h-0.01h-61.7h-0.02v-0.02c-0.91,0-1.75,0.39-2.37,1.01 c-0.61,0.61-1,1.46-1,2.37h0.02v0.01v73.27v0.02h-0.02c0,0.91,0.39,1.75,1.01,2.37c0.61,0.61,1.46,1,2.37,1v-0.02h0.01h61.7h0.02 v0.02c0.91,0,1.75-0.39,2.37-1.01c0.61-0.61,1-1.46,1-2.37h-0.02V108.92L105.18,108.92z"}]]])

(def table-clip-render-fn
  "Wraps v/table-viewer into a react fragment that displays a copy icon
  at the right top corner, that when clicked copies the table to the
  system clipboard."
  `(fn [data opts]
       (let [table (v/table-viewer data opts)]
         (update table :nextjournal/value
                 (fn [table]
                   (let [table-id (random-uuid)
                         ;; The table doesn't come with an id, assign
                         ;; it a unique id.
                         table (assoc table 1 {:id table-id})]
                     [:div.flex 
                      [:div.relative.group.flex-initial
                       [:div.absolute.top-0.right-0.h-6.w-6.invisible.group-hover:visible
                        {:on-click (fn [_e]
                                     (let [range (js/document.createRange)
                                           ct (js/document.getElementById table-id)]
                                       (js/console.log ct)
                                       (.selectNode range ct)
                                       (-> (js/window.getSelection)
                                           (.addRange range))
                                       (js/document.execCommand "copy")
                                       ))}
                        ~copy-icon]
                       table]]))))))

(def a-table
  "A table with 21 rows and a single column with an increasing text
  length."
  (mapv #(hash-map :a (str/join (repeat (* 6 %) "x")))
        (range 21)))

(c/with-viewers
  ;; Replace render-fn of :table viewer.
  [(-> (some #(when (= (:name %) :table) %) (v/get-viewers :root))
       (assoc :render-fn table-clip-render-fn))]
  (v/with-viewer :table
    a-table))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment