penpot/frontend/src/app/main/ui/cursors.clj

91 lines
3 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.cursors
(:require
[app.common.uri :as u]
[clojure.core :as c]
[clojure.java.io :as io]
[cuerdas.core :as str]))
(def cursor-folder "images/cursors")
(def default-hotspot-x 12)
(def default-hotspot-y 12)
(def default-rotation 0)
(def default-height 20)
(defn parse-svg [svg-data]
(-> svg-data
;; Remove the <?xml ?> header
(str/replace #"(?i)<\?xml[^\?]*\?>", "")
;; Remove comments
(str/replace #"<\!\-\-(.*?(?=\-\->))\-\->" "")
;; Remove end of line
(str/replace #"\r?\n|\r" " ")
;; Replace double quotes for single
(str/replace #"\"" "'")
;; Remove the svg root tag
(str/replace #"(?i)<svg.*?>" "")
;; And the closing tag
(str/replace #"(?i)<\/svg>" "")
;; Remove some defs that can be redundant
(str/replace #"<defs.*?/>" "")
;; Unifies the spaces into single space
(str/replace #"\s+" " ")
;; Remove spaces at the beginning of the svg
(str/replace #"^\s+" "")
;; Remove spaces at the end
(str/replace #"\s+$" "")))
(defn encode-svg-cursor
[id rotation x y height]
(let [svg-path (str cursor-folder "/" (name id) ".svg")
data (-> svg-path io/resource slurp parse-svg)
data (u/percent-encode data)
data (if rotation
(str/fmt "%3Cg transform='rotate(%s 8,8)'%3E%s%3C/g%3E" rotation data)
data)]
(str "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='20px' "
"height='" height "px' %3E" data "%3C/svg%3E\") " x " " y ", auto")))
(defmacro cursor-ref
"Creates a static cursor given its name, rotation and x/y hotspot"
([id] (encode-svg-cursor id default-rotation default-hotspot-x default-hotspot-y default-height))
([id rotation] (encode-svg-cursor id rotation default-hotspot-x default-hotspot-y default-height))
([id rotation x y] (encode-svg-cursor id rotation x y default-height))
([id rotation x y height] (encode-svg-cursor id rotation x y height)))
(defmacro cursor-fn
"Creates a dynamic cursor that can be rotated in runtime"
[id initial]
(let [[cp1 cp2] (-> (encode-svg-cursor id "$$$"
default-hotspot-x
default-hotspot-y
default-height)
(str/split #"\$\$\$"))]
`(fn [rot#]
(str/concat ~cp1 (+ ~initial rot#) ~cp2))))
(defmacro collect-cursors
[]
(let [ns-info (:ns &env)]
`(cljs.core/js-obj
~@(->> (:defs ns-info)
(map val)
(filter (fn [entry] (-> entry :meta :cursor)))
(mapcat (fn [{:keys [name] :as entry}]
[(-> name c/name str/camel str/capital) name]))))))