0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-12 15:51:37 -05:00

Add CSS cursor classes

This commit is contained in:
Aitor 2023-06-14 15:45:37 +02:00
parent 0682ed101d
commit 216454f66f
12 changed files with 136 additions and 50 deletions

View file

@ -18,6 +18,7 @@
[app.main.ui :as ui] [app.main.ui :as ui]
[app.main.ui.alert] [app.main.ui.alert]
[app.main.ui.confirm] [app.main.ui.confirm]
[app.main.ui.cursors :as cursors]
[app.main.ui.delete-shared] [app.main.ui.delete-shared]
[app.main.ui.modal :refer [modal]] [app.main.ui.modal :refer [modal]]
[app.main.ui.routes :as rt] [app.main.ui.routes :as rt]
@ -44,6 +45,7 @@
(defn init-ui (defn init-ui
[] []
(cursors/init-styles)
(mf/mount (mf/element ui/app) (dom/get-element "app")) (mf/mount (mf/element ui/app) (dom/get-element "app"))
(mf/mount (mf/element modal) (dom/get-element "modal"))) (mf/mount (mf/element modal) (dom/get-element "modal")))

View file

@ -7,6 +7,8 @@
(ns app.main.ui.cursors (ns app.main.ui.cursors
(:require-macros [app.main.ui.cursors :refer [cursor-ref cursor-fn]]) (:require-macros [app.main.ui.cursors :refer [cursor-ref cursor-fn]])
(:require (:require
[app.common.data.macros :as dm]
[app.util.css :as css]
[app.util.timers :as ts] [app.util.timers :as ts]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -51,6 +53,63 @@
(def resize-ew-2 (cursor-fn :resize-h-2 0)) (def resize-ew-2 (cursor-fn :resize-h-2 0))
(def resize-ns-2 (cursor-fn :resize-h-2 90)) (def resize-ns-2 (cursor-fn :resize-h-2 90))
(defn get-static
[name]
(dm/str "cursor-" name))
(defn get-dynamic
[name rotation]
(dm/str "cursor-" name "-" (.floor js/Math rotation)))
(defn init-static-cursor-style
[style name value]
(.add style (dm/str ".cursor-" name) (js-obj "cursor" (dm/str value " !important"))))
(defn init-dynamic-cursor-style
[style name fn]
(let [rotations (seq (range 0 360 1))]
(doseq [rotation rotations]
(.add style (dm/str ".cursor-" name "-" rotation) (js-obj "cursor" (dm/str (fn rotation) " !important"))))))
(defn init-styles
[]
(let [style (css/create-style)]
;; static
(init-static-cursor-style style "comments" comments)
(init-static-cursor-style style "create-artboard" create-artboard)
(init-static-cursor-style style "create-ellipse" create-ellipse)
(init-static-cursor-style style "create-polygon" create-polygon)
(init-static-cursor-style style "create-rectangle" create-rectangle)
(init-static-cursor-style style "create-shape" create-shape)
(init-static-cursor-style style "duplicate" duplicate)
(init-static-cursor-style style "hand" hand)
(init-static-cursor-style style "move-pointer" move-pointer)
(init-static-cursor-style style "pen" pen)
(init-static-cursor-style style "pen-node" pen-node)
(init-static-cursor-style style "pencil" pencil)
(init-static-cursor-style style "picker" picker)
(init-static-cursor-style style "pointer-inner" pointer-inner)
(init-static-cursor-style style "pointer-move" pointer-move)
(init-static-cursor-style style "pointer-node" pointer-node)
(init-static-cursor-style style "resize-alt" resize-alt)
(init-static-cursor-style style "zoom" zoom)
(init-static-cursor-style style "zoom-in" zoom-in)
(init-static-cursor-style style "zoom-out" zoom-out)
;; dynamic
(init-dynamic-cursor-style style "resize-ew" resize-ew)
(init-dynamic-cursor-style style "resize-nesw" resize-nesw)
(init-dynamic-cursor-style style "resize-ns" resize-ns)
(init-dynamic-cursor-style style "resize-nwse" resize-nwse)
(init-dynamic-cursor-style style "rotate" rotate)
(init-dynamic-cursor-style style "text" text)
(init-dynamic-cursor-style style "scale-ew" scale-ew)
(init-dynamic-cursor-style style "scale-nesw" scale-nesw)
(init-dynamic-cursor-style style "scale-ns" scale-ns)
(init-dynamic-cursor-style style "scale-nwse" scale-nwse)
(init-dynamic-cursor-style style "resize-ew-2" resize-ew-2)
(init-dynamic-cursor-style style "resize-ns-2" resize-ns-2)))
(mf/defc debug-preview (mf/defc debug-preview
{::mf/wrap-props false} {::mf/wrap-props false}
[] []

View file

@ -358,9 +358,9 @@
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture :on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move :on-pointer-move on-pointer-move
:class (when (or hover? selected?)
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
:style {:fill (if (or hover? selected?) distance-color "none") :style {:fill (if (or hover? selected?) distance-color "none")
:cursor (when (or hover? selected?)
(if (= (:resize-axis rect-data) :x) (cur/resize-ew 0) (cur/resize-ew 90)))
:opacity (if selected? 0.5 0.25)}}])) :opacity (if selected? 0.5 0.25)}}]))
(mf/defc padding-rects [{:keys [frame zoom alt? shift?]}] (mf/defc padding-rects [{:keys [frame zoom alt? shift?]}]
@ -661,9 +661,9 @@
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture :on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move :on-pointer-move on-pointer-move
:class (when (or hover? selected?)
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
:style {:fill (if (or hover? selected?) distance-color "none") :style {:fill (if (or hover? selected?) distance-color "none")
:cursor (when (or hover? selected?)
(if (= (:resize-axis rect-data) :x) (cur/resize-ew 0) (cur/resize-ew 90)))
:opacity (if selected? 0.5 0.25)}}])) :opacity (if selected? 0.5 0.25)}}]))
(mf/defc gap-rects [{:keys [frame zoom]}] (mf/defc gap-rects [{:keys [frame zoom]}]

View file

@ -99,10 +99,9 @@
:on-pointer-enter on-enter :on-pointer-enter on-enter
:on-pointer-leave on-leave :on-pointer-leave on-leave
:pointer-events (when-not preview? "visible") :pointer-events (when-not preview? "visible")
:style {:cursor (cond :class (cond (= edit-mode :draw) (cur/get-static "pen-node")
(= edit-mode :draw) cur/pen-node (= edit-mode :move) (cur/get-static "pointer-node"))
(= edit-mode :move) cur/pointer-node) :style {:stroke-width 0
:stroke-width 0
:fill "none"}}]])) :fill "none"}}]]))
(mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode snap-angle?]}] (mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode snap-angle?]}]
@ -160,8 +159,8 @@
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:on-pointer-enter on-enter :on-pointer-enter on-enter
:on-pointer-leave on-leave :on-pointer-leave on-leave
:style {:cursor (when (= edit-mode :move) cur/pointer-move) :class (when (= edit-mode :move) (cur/get-static "pointer-move"))
:fill "none" :style {:fill "none"
:stroke-width 0}}]]))) :stroke-width 0}}]])))
(mf/defc path-preview [{:keys [zoom command from]}] (mf/defc path-preview [{:keys [zoom command from]}]

View file

@ -222,8 +222,7 @@
[:div.text-editor [:div.text-editor
{:ref self-ref {:ref self-ref
:style {:cursor (cur/text (:rotation shape)) :style {:width (:width shape)
:width (:width shape)
:height (:height shape) :height (:height shape)
;; We hide the editor when is blurred because otherwise the selection won't let us see ;; We hide the editor when is blurred because otherwise the selection won't let us see
;; the underlying text. Use opacity because display or visibility won't allow to recover ;; the underlying text. Use opacity because display or visibility won't allow to recover
@ -231,6 +230,7 @@
:opacity (when @blurred 0)} :opacity (when @blurred 0)}
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:class (dom/classnames :class (dom/classnames
(cur/get-dynamic "text" (:rotation shape)) true
:align-top (= (:vertical-align content "top") "top") :align-top (= (:vertical-align content "top") "top")
:align-center (= (:vertical-align content) "center") :align-center (= (:vertical-align content) "center")
:align-bottom (= (:vertical-align content) "bottom"))} :align-bottom (= (:vertical-align content) "bottom"))}

View file

@ -330,8 +330,8 @@
:key (str "viewport" page-id) :key (str "viewport" page-id)
:view-box (utils/format-viewbox vbox) :view-box (utils/format-viewbox vbox)
:ref on-viewport-ref :ref on-viewport-ref
:class (when drawing-tool "drawing") :class (dm/str @cursor (when drawing-tool "drawing"))
:style {:cursor @cursor :touch-action "none"} :style {:touch-action "none"}
:fill "none" :fill "none"
:on-click on-click :on-click on-click

View file

@ -15,7 +15,6 @@
[app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.grid-layout.editor :as dwge]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.cursors :as cur]
[app.util.dom :as dom] [app.util.dom :as dom]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -230,15 +229,15 @@
[:rect.resize-handler [:rect.resize-handler
{:x x {:x x
:y y :y y
:class (if (= type :column)
"resize-ew-0"
"resize-ns-0")
:height height :height height
:width width :width width
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture :on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move :on-pointer-move on-pointer-move
:style {:fill "transparent" :style {:fill "transparent"}}]))
:cursor (if (= type :column)
(cur/resize-ew 0)
(cur/resize-ns 0))}}]))
(mf/defc editor (mf/defc editor
{::mf/wrap-props false} {::mf/wrap-props false}

View file

@ -302,9 +302,9 @@
:y y :y y
:width width :width width
:height height :height height
:class (if (= axis :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ns" 0))
:style {:fill "none" :style {:fill "none"
:pointer-events (if frame-guide-outside? "none" "fill") :pointer-events (if frame-guide-outside? "none" "fill")}
:cursor (if (= axis :x) (cur/resize-ew 0) (cur/resize-ns 0))}
:on-pointer-enter on-pointer-enter :on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave :on-pointer-leave on-pointer-leave
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
@ -417,9 +417,9 @@
:on-pointer-up on-pointer-up :on-pointer-up on-pointer-up
:on-lost-pointer-capture on-lost-pointer-capture :on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move :on-pointer-move on-pointer-move
:class (if (= axis :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ns" 0))
:style {:fill "none" :style {:fill "none"
:pointer-events "fill" :pointer-events "fill"}}]))
:cursor (if (= axis :x) (cur/resize-ew 0) (cur/resize-ns 0))}}]))
(when (:new-position @state) (when (:new-position @state)
[:& guide {:guide {:axis axis [:& guide {:guide {:axis axis

View file

@ -206,7 +206,7 @@
[:div.pixel-overlay [:div.pixel-overlay
{:id "pixel-overlay" {:id "pixel-overlay"
:tab-index 0 :tab-index 0
:style {:cursor cur/picker} :class (cur/get-static "picker")
:on-pointer-down handle-pointer-down-picker :on-pointer-down handle-pointer-down-picker
:on-pointer-up handle-pointer-up-picker :on-pointer-up handle-pointer-up-picker
:on-pointer-move handle-pointer-move-picker} :on-pointer-move handle-pointer-move-picker}

View file

@ -165,9 +165,9 @@
:top-right 90 :top-right 90
:bottom-right 180 :bottom-right 180
:bottom-left 270)] :bottom-left 270)]
[:rect {:style {:cursor (cur/rotate (+ rotation angle))} [:rect {:x x
:x x
:y y :y y
:class (cur/get-dynamic "rotate" (+ rotation angle))
:width size :width size
:height size :height size
:fill (if (debug? :handlers) "blue" "none") :fill (if (debug? :handlers) "blue" "none")
@ -180,8 +180,8 @@
(let [layout (mf/deref refs/workspace-layout) (let [layout (mf/deref refs/workspace-layout)
scale-text (:scale-text layout) scale-text (:scale-text layout)
cursor (if (#{:top-left :bottom-right} position) cursor (if (#{:top-left :bottom-right} position)
(if scale-text (cur/scale-nesw rotation) (cur/resize-nesw rotation)) (if scale-text (cur/get-dynamic "scale-nesw" rotation) (cur/get-dynamic "resize-nesw" rotation))
(if scale-text (cur/scale-nwse rotation) (cur/resize-nwse rotation))) (if scale-text (cur/get-dynamic "scale-nwse" rotation) (cur/get-dynamic "resize-nwse" rotation)))
{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)] {cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
[:g.resize-handler [:g.resize-handler
@ -203,21 +203,21 @@
{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)] {cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
[:rect {:x cx' [:rect {:x cx'
:y cy' :y cy'
:class cursor
:width resize-point-circle-radius :width resize-point-circle-radius
:height resize-point-circle-radius :height resize-point-circle-radius
:transform (when rotation (dm/fmt "rotate(%, %, %)" rotation cx' cy')) :transform (when rotation (dm/fmt "rotate(%, %, %)" rotation cx' cy'))
:style {:fill (if (debug? :handlers) "red" "none") :style {:fill (if (debug? :handlers) "red" "none")
:stroke-width 0 :stroke-width 0}
:cursor cursor}
:on-pointer-down #(on-resize {:x cx' :y cy'} %)}]) :on-pointer-down #(on-resize {:x cx' :y cy'} %)}])
[:circle {:on-pointer-down #(on-resize {:x cx' :y cy'} %) [:circle {:on-pointer-down #(on-resize {:x cx' :y cy'} %)
:r (/ resize-point-circle-radius zoom) :r (/ resize-point-circle-radius zoom)
:cx cx' :cx cx'
:cy cy' :cy cy'
:class cursor
:style {:fill (if (debug? :handlers) "red" "none") :style {:fill (if (debug? :handlers) "red" "none")
:stroke-width 0 :stroke-width 0}}])]))
:cursor cursor}}])]))
(mf/defc resize-side-handler (mf/defc resize-side-handler
"The side handler is always rendered horizontally and then rotated" "The side handler is always rendered horizontally and then rotated"
@ -246,13 +246,13 @@
:y target-y :y target-y
:width length :width length
:height height :height height
:class (if (#{:left :right} position)
(if scale-text (cur/get-dynamic "scale-ew" rotation) (cur/get-dynamic "resize-ew" rotation))
(if scale-text (cur/get-dynamic "scale-ns" rotation) (cur/get-dynamic "resize-ns" rotation)))
:transform transform-str :transform transform-str
:on-pointer-down #(on-resize res-point %) :on-pointer-down #(on-resize res-point %)
:style {:fill (if (debug? :handlers) "yellow" "none") :style {:fill (if (debug? :handlers) "yellow" "none")
:stroke-width 0 :stroke-width 0}}]]))
:cursor (if (#{:left :right} position)
(if scale-text (cur/scale-ew rotation) (cur/resize-ew rotation))
(if scale-text (cur/scale-ns rotation) (cur/resize-ns rotation))) }}]]))
(defn minimum-selrect [{:keys [x y width height] :as selrect}] (defn minimum-selrect [{:keys [x y width height] :as selrect}]
(let [final-width (max width min-selrect-side) (let [final-width (max width min-selrect-side)

View file

@ -20,19 +20,19 @@
(defn get-cursor [cursor] (defn get-cursor [cursor]
(case cursor (case cursor
:hand cur/hand :hand (cur/get-static "hand")
:comments cur/comments :comments (cur/get-static "comments")
:create-artboard cur/create-artboard :create-artboard (cur/get-static "create-artboard")
:create-rectangle cur/create-rectangle :create-rectangle (cur/get-static "create-rectangle")
:create-ellipse cur/create-ellipse :create-ellipse (cur/get-static "create-ellipse")
:pen cur/pen :pen (cur/get-static "pen")
:pencil cur/pencil :pencil (cur/get-static "pencil")
:create-shape cur/create-shape :create-shape (cur/get-static "create-shape")
:duplicate cur/duplicate :duplicate (cur/get-static "duplicate")
:zoom cur/zoom :zoom (cur/get-static "zoom")
:zoom-in cur/zoom-in :zoom-in (cur/get-static "zoom-in")
:zoom-out cur/zoom-out :zoom-out (cur/get-static "zoom-out")
cur/pointer-inner)) (cur/get-static "pointer-inner")))
;; Ensure that the label has always the same font ;; Ensure that the label has always the same font
;; size, regardless of zoom ;; size, regardless of zoom

View file

@ -0,0 +1,27 @@
(ns app.util.css
(:require
[app.common.data.macros :as dm]
[app.util.dom :as dom]))
(defn declarations->str
"Converts an object of CSS declarations to a string"
[declarations]
(let [entries (.from js/Array (.entries js/Object declarations))]
(.reduce entries (fn [acc [k v]]
(dm/str acc k ": " v ";")) "")))
(defn add-rule
"Adds a CSS rule to a CSS Style Sheet"
[styleSheet selector declarations]
(.insertRule styleSheet (dm/str selector " {" (declarations->str declarations) "}")))
;; FIXME: Maybe we should rename this to `create-dynamic-style`?
(defn create-style
"Creates a new CSS Style Sheet and returns an object that allows adding rules to it"
[]
(let [style (dom/create-element "style")]
(dom/set-attribute! style "type" "text/css")
(dom/append-child! js/document.head style)
(js-obj "add" (partial add-rule (.-sheet style)))))