0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-25 08:16:49 -05:00

Merge pull request #233 from uxbox/253/custom_cursors

Custom cursors
This commit is contained in:
Andrey Antukh 2020-06-02 14:23:26 +02:00 committed by GitHub
commit d6c97f9d19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 271 additions and 32 deletions

View file

@ -33,6 +33,9 @@
thheller/shadow-cljs {:mvn/version "2.8.110"}
lambdaisland/uri {:mvn/version "1.3.45"
:exclusions [org.clojure/data.json]}
;; i18n parsing
carocad/parcera {:mvn/version "0.11.0"}
org.antlr/antlr4-runtime {:mvn/version "4.7"}}}

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="24px" height="24px">
<path fill="#231F20" fill-rule="evenodd" d="M5 5H1v1h5V1H5v4zm4-1v5H4v1h5v5h1v-5h5V9h-5V4H9z" clip-rule="evenodd"/>
<path fill="#fff" fill-rule="evenodd" d="M.5 4.5h4v-4h2v6h-6v-2zM5 5H1v1h5V1H5v4zm3.5-1.5h2v5h5v2h-5v5h-2v-5h-5v-2h5v-5zM9 9H4v1h5v5h1v-5h5V9h-5V4H9v5z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 390 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#231F20" fill-rule="evenodd" d="M15 4a3 3 0 11-6 0 3 3 0 016 0zm-1 0a2 2 0 11-4 0 2 2 0 014 0z" clip-rule="evenodd"/>
<path fill="#231F20" d="M6 4v5H1v1h5v5h1v-5h5V9H7V4H6z"/>
<path fill="#fff" fill-rule="evenodd" d="M5.5 3.5h2v5h5v2h-5v5h-2v-5h-5v-2h5v-5zM6 9H1v1h5v5h1v-5h5V9H7V4H6v5zm6-3.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM15.5 4a3.5 3.5 0 11-7 0 3.5 3.5 0 017 0zM12 7a3 3 0 100-6 3 3 0 000 6zm0-1a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 557 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 16">
<defs/>
<path fill="#231F20" d="M6 4v5H1v1h5v5h1v-5h5V9H7V4H6z"/>
<path fill="#fff" fill-rule="evenodd" d="M5.5 3.5h2v5h5v2h-5v5h-2v-5h-5v-2h5v-5zM6 9H1v1h5v5h1v-5h5V9H7V4H6v5z" clip-rule="evenodd"/>
<path fill="#000" stroke="#fff" stroke-width=".5" d="M12.2.9L12 .5l-.3.4-3.4 6-.2.3H15.9l-.3-.3-3.4-6zm1 4.8h-2.5L12 3.5l1.3 2.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 421 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#231F20" fill-rule="evenodd" d="M9 7V1h6v6H9zm1-5h4v4h-4V2z" clip-rule="evenodd"/>
<path fill="#231F20" d="M6 4v5H1v1h5v5h1v-5h5V9H7V4H6z"/>
<path fill="#fff" fill-rule="evenodd" d="M8.5.5h7v7h-7v-7zm-3 3h2v5h5v2h-5v5h-2v-5h-5v-2h5v-5zM6 9H1v1h5v5h1v-5h5V9H7V4H6v5zm4-7h4v4h-4V2zm.5 3.5h3v-3h-3v3zM9 7V1h6v6H9z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 442 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#231F20" fill-rule="evenodd" d="M7 7V1h1v6h6v1H8v6H7V8H1V7h6z" clip-rule="evenodd"/>
<path fill="#fff" fill-rule="evenodd" d="M6.5.5h2v6h6v2h-6v6h-2v-6h-6v-2h6v-6zM7 7H1v1h6v6h1V8h6V7H8V1H7v6z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 322 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path fill="#fff" d="M5.1 0L16 10.9H9.2L5.1 15z"/>
<path d="M5.7 1.5l8.7 8.6H9l-3.3 3.4zm3 7.9h4L6.5 3.3v8.4z"/>
<path fill="#fff" d="M0 0v15l4.1-4.1h2.4v.8l.8-.8h3.6L9.3 9.5h3.4L6.5 3.3v3.2zm1 2.3l5.4 5.5v2.1H3.7L1 12.7zm7.4 7.4l.2.2h-.4z"/>
<path d="M1 12.7V2.3l7.7 7.6h-5z"/>
</svg>

After

Width:  |  Height:  |  Size: 355 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><defs/><path fill="#fff" d="M3.5 5.38v-3c.5-1 2.5-1 3 0v-1c.5-1 2.5-1 3 0v1c.5-1 2.5-1 3 0v1c.8-.24 1.24-.15 2 .5v7.5c0 3-3 4-3 4h-7s-3-1.15-3-4v-5.5c.82-.86 1.25-.9 2-.5z"/><path fill="#000" d="M8 0c-.73 0-1.6.29-1.98 1.04a.51.51 0 00-.05.17c-.33-.13-.69-.2-1.02-.2-.73 0-1.6.3-1.98 1.05a.5.5 0 00-.05.23V4.6a1.49 1.49 0 00-.59 0c-.47.1-.87.43-1.3.88a.51.51 0 00-.14.35v5.59c0 1.65.87 2.8 1.7 3.5a6.54 6.54 0 001.52.97l.1.05.04.01h.01a.5.5 0 00.18.04h7.11c.06 0 .11 0 .17-.03h.01l.03-.01.12-.05A5.4 5.4 0 0013.4 15c.84-.7 1.71-1.84 1.71-3.56V3.8a.5.5 0 00-.18-.39 3 3 0 00-1.2-.7 1.95 1.95 0 00-.65-.04v-.4a.5.5 0 00-.05-.22c-.38-.75-1.25-1.04-1.98-1.04-.33 0-.7.06-1.02.2a.51.51 0 00-.05-.18C9.6.3 8.73 0 8 0zm0 1.02c.48 0 .85.17 1.02.39v3.92h1.01V2.45c.16-.23.52-.42 1.02-.42.48 0 .84.18 1.01.4v2.9h1.02V3.71c.16-.02.3-.02.4 0 .17.04.35.14.62.34v7.38c0 1.33-.66 2.21-1.35 2.78a4.98 4.98 0 01-1.25.76l-.04.01H4.52a4.75 4.75 0 01-1.27-.82 3.55 3.55 0 01-1.35-2.73V6.05c.32-.31.51-.41.65-.44.08-.02.2 0 .37.06v2.71h1.02V2.43c.16-.22.53-.4 1.01-.4.5 0 .86.19 1.02.42v2.88h1.01V1.41c.17-.22.54-.4 1.02-.4z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<path fill="#fff" d="M8 .3L5.3 3l1.2 1.2.5-.5v2.8H3.7l.5-.5L3 4.8.3 7.5 3 10.2 4.2 9l-.5-.5H7v3.8l-.5-.6L5.3 13 8 15.7l2.7-2.7-1.2-1.2-.5.5V8.5h3.3l-.5.5 1.2 1.2 2.7-2.7L13 4.8 11.8 6l.5.5H9V3.7l.5.5L10.7 3zM8 11v3.2z" />
<path fill="#000" d="M8 1L6 3l.5.5 1-1V7h-5l1-1-.5-.5-2 2 2 2 .5-.5-1-1h5v5.5l-1-1-.5.5 2 2 2-2-.5-.5-1 1V8h5l-1 1 .5.5 2-2-2-2-.5.5 1 1h-5V2.5l1 1L10 3 8 1z"/>
<path fill="#fff" d="M8 .3L5.3 3l1.2 1.2.5-.5v2.8H3.7l.5-.5L3 4.8.3 7.5 3 10.2 4.2 9l-.5-.5H7v3.8l-.5-.5L5.3 13 8 15.7l2.7-2.7-1.2-1.2-.5.5V8.5h3.3l-.5.5 1.2 1.2 2.7-2.7L13 4.8 11.8 6l.5.5H9V3.7l.5.5L10.7 3 8 .3zM8 1l2 2-.5.5-1-1V7h5l-1-1 .5-.5 2 2-2 2-.5-.5 1-1h-5v5.5l1-1 .5.5-2 2-2-2 .5-.5 1 1V8h-5l1 1-.5.5-2-2 2-2 .5.5-1 1h5V2.5l-1 1L6 3l2-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 808 B

View file

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#fff" d="M12.7 8.6l3 3-4 4.1-3-3c-.7.2 0 .3-.7.3-1.7 0-3.1-.6-4.1-1.7-1-1-1.7-2.5-1.9-4.2a46 46 0 00-1-4.7v-.1L.5.5l1.8.5a17.4 17.4 0 001.3.4L7 2c1.7.2 3.2.8 4.2 1.9C12.4 4.9 13 6.3 13 8l-.3.6z"/>
<path fill="#231F20" fill-rule="evenodd" d="M12.7 8.6l3 3-4 4.1-3-3 .7-.7 2.3 2.3 2.6-2.6L12 9.4l.7-.8z" clip-rule="evenodd"/>
<path fill="#231F20" d="M2.3 1a17.4 17.4 0 001.3.4L7 2c1.7.2 3.2.8 4.2 1.9C12.4 4.9 13 6.3 13 8c0 .8 0 1.5-.3 2.1l-.8-.8L12 8c0-2.9-2-4.6-5-5a46.4 46.4 0 01-5-1l.3 1.1c.2.9.5 2.2.7 3.9.4 3 2.1 5 5 5h1.3l.8.7c-.6.2-1.3.3-2.1.3-1.7 0-3.1-.6-4.1-1.7-1-1-1.7-2.5-1.9-4.2a46 46 0 00-1-4.7v-.1L.5.5l1.8.5z"/>
<path fill="#231F20" fill-rule="evenodd" d="M1.6 1.6c.2-.1.6-.1.8 0l5 5a.5.5 0 11-.8.8l-5-5a.5.5 0 010-.8z" clip-rule="evenodd"/>
<path fill="#231F20" d="M7.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"/>
</svg>

After

Width:  |  Height:  |  Size: 936 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 17"><defs/><path fill="#fff" d="M11.4.4L0 11.8v4.6h4.6L16 5 11.4.4z"/><path fill="#000" d="M11.4.4L0 11.8v4.6h4.6L16 5 11.4.4zm0 1.5L12.5 3 2.6 13l-1.1-1.2 9.9-10zm2 2L14.5 5 4.6 15l-1.2-1.2 10-10zM1 12.9l2.4 2.3H1v-2.4z"/></svg>

After

Width:  |  Height:  |  Size: 298 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 18">
<defs/>
<path fill="#fff" d="M1 14.6V3.4l8.2 8.2H4l-3 3z"/>
<path fill="#231F20" fill-rule="evenodd" d="M0 17V1l11.6 11.6H4.4L0 17zm4-5.4h5.2L1 3.4v11.2l3-3z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 267 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#231F20" fill-rule="evenodd" d="M15 1h-3.9v.8h2.5L8 7.5 2.3 1.8H5V1H1v4h.8V2.3L7.5 8l-5.7 5.6v-2.5H1V15h3.9v-.8H2.4L8 8.5l5.6 5.7h-2.5v.8H15v-3.9h-.8v2.5L8.5 8l5.7-5.6v2.5h.8V1z" clip-rule="evenodd"/>
<path fill="#fff" fill-rule="evenodd" d="M15.5.5v4.9h-1.8V3.6L9.2 8l4.5 4.4v-1.8h1.8v4.9h-4.9v-1.8h1.8L8 9.2l-4.4 4.5h1.8v1.8H.5v-4.9h1.8v1.8L6.8 8 2.3 3.5v2H.5v-5h4.9v1.8H3.5L8 6.8l4.4-4.5h-1.8V.5h4.9zm-1.9 1.3L8 7.5 2.3 1.8H5V1H1v4h.8V2.3L7.5 8l-5.7 5.6v-2.5H1V15h3.9v-.8H2.4L8 8.5l5.6 5.7h-2.5v.8H15v-3.9h-.8v2.5L8.5 8l5.7-5.6v2.5h.8V1h-3.9v.8h2.5z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 682 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#231F20" fill-rule="evenodd" d="M11.1 1H15v3.9h-.8V2.4L2.4 14.2h2.5v.8H1v-3.9h.8v2.5L13.6 1.8h-2.5V1z" clip-rule="evenodd"/>
<path fill="#fff" fill-rule="evenodd" d="M15.5.5v4.9h-1.8V3.6l-10.1 10h1.8v1.9H.5v-4.9h1.8v1.8l10.1-10h-1.8V.4h4.9zm-1.9 1.3L1.8 13.6v-2.5H1V15h3.9v-.8H2.4L14.2 2.4v2.5h.8V1h-3.9v.8h2.5z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><defs/><path fill="#fff" d="M2.6 5.6L0 8.3 2.6 11l1.2-1.2-.5-.5h9.4l-.5.5 1.2 1.2L16 8.3l-2.6-2.7-1.2 1.2.5.5H3.3l.5-.5-1.2-1.2z"/><path fill="#231f20" d="M5.1 279h-4v1h5v-5h-1zm5 0v5h-5v1h5v5h1v-5h5v-1h-5v-5z"/><path fill="#fff" d="M.6 278.5h4v-4h2v6h-6zm4.5.5h-4v1h5v-5h-1zm4.5-.5h2v5h5v2h-5v5h-2v-5h-5v-2h5zm.5 5.5h-5v1h5v5h1v-5h5v-1h-5v-5h-1z"/><path fill="#000" d="M2.6 6.3l-2 2 2 2 .6-.5-1-1H14l-1 1 .5.5 2-2-2-2-.5.5 1 1H2.1l1-1-.5-.5z"/></svg>

After

Width:  |  Height:  |  Size: 523 B

View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
<defs/>
<path fill="#231F20" d="M1 3.7L3.7 1l2.7 2.7-.6.6-1.7-1.8h-.8L1.6 4.3 1 3.7zM12.3 15l2.7-2.7-2.8-2.8-.6.6 1.9 1.8v.8l-1.8 1.7.6.6z"/>
<path fill="#231F20" d="M3.2 1.9v.4A10.5 10.5 0 0014 12.8v-.9h-.4A9.6 9.6 0 014.1 2H3z"/>
<path fill="#fff" fill-rule="evenodd" d="M3.7.3L7 3.7 5.8 5 4.7 3.8a9.1 9.1 0 007.4 7.5L11 10.1l1.3-1.3 3.5 3.5-3.4 3.4-1.3-1.3 1.2-1.2a11 11 0 01-9.4-9.4L1.6 5 .3 3.7 3.7.3zm-.5 2.3v.7a10.5 10.5 0 0010.1 9.5l-1.6 1.6.6.6 2.7-2.7-2.8-2.8-.6.6 1.9 1.8a133.5 133.5 0 00-.7 0A9.6 9.6 0 014 2.5l1.8 1.8.6-.6L3.7 1 1 3.7l.6.6 1.6-1.7z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 673 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16"><defs/><path fill="#fff" d="M5 .5v2c1.5 0 2 .9 2 2v7c0 1.1-.5 2-2 2v2c1.5 0 2.4-.4 3-1 .6.6 1.5 1 3 1v-2c-1.5 0-2-.9-2-2v-7c0-1.1.5-2 2-2v-2c-1.5 0-2.4.4-3 1-.6-.6-1.5-1-3-1z"/><path fill="#231f20" d="M5.5 1v1a2 2 0 012 2v8a2 2 0 01-2 2v1c1 0 2-.5 2.5-1.3a3 3 0 002.5 1.3v-1a2 2 0 01-2-2V4c0-1.1.9-2 2-2V1c-1 0-2 .5-2.5 1.3A3 3 0 005.5 1z"/></svg>

After

Width:  |  Height:  |  Size: 419 B

View file

@ -1,9 +1,14 @@
.debug-preview {
max-height: 100vh;
display: flex;
flex-direction: column;
overflow: scroll;
}
.debug-icons-preview {
display: flex;
flex-wrap: wrap;
overflow: scroll;
.icon-item {
.icon-item, .cursor-item {
padding: 10px;
display: flex;
flex-direction: column;
@ -17,4 +22,7 @@
height: 100%;
}
}
.cursor-item {
height: auto;
}
}

View file

@ -145,15 +145,6 @@
g.controls {
rect.main { pointer-events: none; }
circle.rotate { cursor: ns-resize; } // TODO
.top-left { cursor: nwse-resize; }
.bottom-right { cursor: nwse-resize; }
.top-right { cursor: nesw-resize; }
.bottom-left { cursor: nesw-resize; }
.top { cursor: ns-resize; }
.bottom { cursor: ns-resize; }
.left { cursor: ew-resize; }
.right { cursor: ew-resize; }
}
}

View file

@ -315,6 +315,14 @@
(reduce reduce-fn state groups-to-adjust)))))
(defn start-pan [state]
(-> state
(assoc-in [:workspace-local :panning] true)))
(defn finish-pan [state]
(-> state
(update :workspace-local dissoc :panning)))
;; --- Toggle layout flag

View file

@ -204,7 +204,7 @@
(rx/take-until stopper)
(rx/map #(gpt/to-vec initial %))
(rx/map #(gpt/length %))
(rx/filter #(> % 0.5))
(rx/filter #(> % 1))
(rx/take 1)
(rx/map #(start-move initial selected)))))))

View file

@ -23,6 +23,7 @@
[uxbox.main.ui.auth :refer [auth verify-token]]
[uxbox.main.ui.dashboard :refer [dashboard]]
[uxbox.main.ui.icons :as i]
[uxbox.main.ui.cursors :as c]
[uxbox.main.ui.messages :as msgs]
[uxbox.main.ui.settings :as settings]
[uxbox.main.ui.static :refer [not-found-page not-authorized-page]]
@ -106,7 +107,12 @@
:debug-icons-preview
(when *assert*
[:& i/debug-icons-preview])
[:div.debug-preview
[:h1 "Cursors"]
[:& c/debug-preview]
[:h1 "Icons"]
[:& i/debug-icons-preview]
])
(:dashboard-search
:dashboard-team

View file

@ -0,0 +1,77 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.cursors
(:import java.net.URLEncoder)
(:require [rumext.alpha]
[clojure.java.io :as io]
[lambdaisland.uri.normalize :as uri]
[cuerdas.core :as str]))
(def cursor-folder "images/cursors")
(def default-hotspot-x 12)
(def default-hotspot-y 12)
(def default-rotation 0)
(defn parse-svg [svg-data]
(-> svg-data
;; Remove the <?xml ?> header
(str/replace #"(?i)<\?xml[^\?]*\?>", "")
;; Remove comments
(str/replace #"<\!\-\-(.*?(?=\-\->))\-\->" "")
;; Remofe 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]
(let [svg-path (str cursor-folder "/" (name id) ".svg")
data (-> svg-path io/resource slurp parse-svg uri/percent-encode)
transform (if rotation (str " transform='rotate(" rotation ")'") "")
data (clojure.pprint/cl-format
nil
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='24px' height='24px'~A%3E~A%3C/svg%3E\") ~A ~A, auto"
transform data x y)]
data))
(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))
([id rotation] (encode-svg-cursor id rotation default-hotspot-x default-hotspot-y))
([id rotation x y] (encode-svg-cursor id rotation x y)))
(defmacro cursor-fn
"Creates a dynamic cursor that can be rotated in runtime"
[id initial]
(let [cursor (encode-svg-cursor id "{{rotation}}" default-hotspot-x default-hotspot-y)]
`(fn [rot#]
(str/replace ~cursor "{{rotation}}" (+ ~initial rot#)))))

View file

@ -0,0 +1,56 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.cursors
(:require-macros [uxbox.main.ui.cursors :refer [cursor-ref
cursor-fn]])
(:require [rumext.alpha :as mf]
[cuerdas.core :as str]
[uxbox.util.timers :as ts]))
(def create-artboard (cursor-ref :create-artboard))
(def create-ellipse (cursor-ref :create-ellipse))
(def create-polygon (cursor-ref :create-polygon))
(def create-rectangle (cursor-ref :create-reclangle))
(def create-shape (cursor-ref :create-shape))
(def duplicate (cursor-ref :duplicate 0 0 0))
(def hand (cursor-ref :hand))
(def move-pointer (cursor-ref :move-pointer))
(def pencil (cursor-ref :pencil 0 0 24))
(def pen (cursor-ref :pen 0 0 0))
(def pointer-inner (cursor-ref :pointer-inner 0 0 0))
(def resize-alt (cursor-ref :resize-alt))
(def resize-nesw (cursor-fn :resize-h 45))
(def resize-nwse (cursor-fn :resize-h 135))
(def resize-ew (cursor-fn :resize-h 0))
(def resize-ns (cursor-fn :resize-h 90))
(def rotate (cursor-fn :rotate 90))
(def text (cursor-ref :text))
(mf/defc debug-preview
{::mf/wrap-props false}
[props]
(let [rotation (mf/use-state 0)]
(mf/use-effect (fn [] (ts/interval 100 #(reset! rotation inc))))
[:section.debug-icons-preview
(for [[key val] (sort-by first (ns-publics 'uxbox.main.ui.cursors))]
(when (not= key 'debug-icons-preview)
(let [value (deref val)
value (if (fn? value) (value @rotation) value)]
[:div.cursor-item {:key key}
[:div {:style {:width "100px"
:height "100px"
:background-image (-> value (str/replace #"(url\(.*\)).*" "$1"))
:background-size "cover"
:cursor value}}]
[:span {:style {:white-space "nowrap"
:margin-right "1rem"}} (pr-str key)]])))]))

View file

@ -19,6 +19,7 @@
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.streams :as ms]
[uxbox.main.ui.cursors :as cur]
[uxbox.util.dom :as dom]
[uxbox.util.object :as obj]
[uxbox.common.geom.shapes :as geom]
@ -26,8 +27,6 @@
[uxbox.common.geom.matrix :as gmt]
[uxbox.util.debug :refer [debug?]]))
(defn rotation-cursor [angle]
(str "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20px' height='20px' transform='rotate(" angle ")' viewBox='0 0 132.292 132.006'%3E%3Cpath d='M85.225 3.48c.034 4.989-.093 9.852-.533 14.78-29.218 5.971-54.975 27.9-63.682 56.683-1.51 2.923-1.431 7.632-3.617 9.546-5.825.472-11.544.5-17.393.45 11.047 15.332 20.241 32.328 32.296 46.725 5.632 1.855 7.155-5.529 10.066-8.533 8.12-12.425 17.252-24.318 24.269-37.482-6.25-.86-12.564-.88-18.857-1.057 5.068-17.605 19.763-31.81 37.091-37.122.181 6.402.206 12.825 1.065 19.184 15.838-9.05 30.899-19.617 45.601-30.257 2.985-4.77-3.574-7.681-6.592-9.791C111.753 17.676 98.475 8.889 85.23.046l-.005 3.435z'/%3E%3Cpath fill='%23fff' d='M92.478 23.995s-1.143.906-6.714 1.923c-29.356 5.924-54.352 30.23-59.717 59.973-.605 3.728-1.09 5.49-1.09 5.49l-11.483-.002s7.84 10.845 10.438 15.486c3.333 4.988 6.674 9.971 10.076 14.912a2266.92 2266.92 0 0019.723-29.326c-5.175-.16-10.35-.343-15.522-.572 3.584-27.315 26.742-50.186 53.91-54.096.306 5.297.472 10.628.631 15.91a2206.462 2206.462 0 0029.333-19.726c-9.75-6.7-19.63-13.524-29.483-20.12z'/%3E%3C/svg%3E\") 10 10, auto"))
(def rotation-handler-size 25)
(def resize-point-radius 4)
@ -115,7 +114,7 @@
:top-right 90
:bottom-right 180
:bottom-left 270)]
[:rect {:style {:cursor (rotation-cursor (+ rotation angle))}
[:rect {:style {:cursor (cur/rotate (+ rotation angle))}
:x x
:y y
:width size
@ -124,7 +123,7 @@
:transform transform
:on-mouse-down on-rotate}]))
(mf/defc resize-point-handler [{:keys [cx cy zoom position on-resize transform]}]
(mf/defc resize-point-handler [{:keys [cx cy zoom position on-resize transform rotation]}]
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)
rot-square (case position
:top-left 0
@ -132,34 +131,36 @@
:bottom-right 180
:bottom-left 270)]
[:g.resize-handler
[:circle {:class (name position)
:r (/ resize-point-radius zoom)
[:circle {:r (/ resize-point-radius zoom)
:style {:fillOpacity "1"
:strokeWidth "1px"
:vectorEffect "non-scaling-stroke"}
:vectorEffect "non-scaling-stroke"
}
:fill "#FFFFFF"
:stroke "#1FDEA7"
:cx cx'
:cy cy'}]
[:rect {:class (name position)
:x cx
[:rect {:x cx
:y cy
:width (/ resize-point-rect-size zoom)
:height (/ resize-point-rect-size zoom)
:fill (if (debug? :resize-handler) "red" "transparent")
:on-mouse-down on-resize
:style {:cursor (if (#{:top-left :bottom-right} position)
(cur/resize-nesw rotation) (cur/resize-nwse rotation))}
:transform (gmt/multiply transform
(gmt/rotate-matrix rot-square (gpt/point cx cy)))}]
[:circle {:class (name position)
:on-mouse-down on-resize
[:circle {:on-mouse-down on-resize
:r (/ resize-point-circle-radius zoom)
:fill (if (debug? :resize-handler) "red" "transparent")
:cx cx'
:cy cy'}]
:cy cy'
:style {:cursor (if (#{:top-left :bottom-right} position)
(cur/resize-nesw rotation) (cur/resize-nwse rotation))}}]
]))
(mf/defc resize-side-handler [{:keys [x y length angle zoom position transform on-resize]}]
(mf/defc resize-side-handler [{:keys [x y length angle zoom position rotation transform on-resize]}]
[:rect {:x (+ x (/ resize-point-rect-size zoom))
:y (- y (/ resize-side-height 2 zoom))
:width (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
@ -169,8 +170,8 @@
:on-mouse-down on-resize
:style {:fill (if (debug? :resize-handler) "yellow" "transparent")
:cursor (if (#{:left :right} position)
"ew-resize"
"ns-resize") }}])
(cur/resize-ew rotation)
(cur/resize-ns rotation)) }}])
(mf/defc controls
{::mf/wrap-props false}
@ -244,7 +245,7 @@
:on-mouse-down #(on-mouse-down % index)
:fill "#ffffff"
:stroke "#1FDEA7"
:style {:cursor "pointer"}}]))])))
:style {:cursor cur/move-pointer}}]))])))
;; TODO: add specs for clarity

View file

@ -48,6 +48,7 @@
(when-not (or (empty? selected) (kbd/shift? event))
(st/emit! dw/deselect-all))
(st/emit! (dw/select-shape id))))
(st/emit! (dw/start-move-selected)))))))
(defn on-context-menu

View file

@ -18,6 +18,7 @@
[uxbox.main.data.workspace.texts :as dwt]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
[uxbox.main.ui.cursors :as cur]
[uxbox.main.ui.workspace.shapes.common :as common]
[uxbox.main.ui.shapes.text :as text]
[uxbox.main.ui.keyboard :as kbd]
@ -301,6 +302,7 @@
:spell-check "false"
:on-focus on-focus
:class "rich-text"
:style {:cursor cur/text}
:render-element render-element
:render-leaf render-text
:on-mouse-up on-mouse-up

View file

@ -15,6 +15,7 @@
[potok.core :as ptk]
[rumext.alpha :as mf]
[uxbox.main.ui.icons :as i]
[uxbox.main.ui.cursors :as cur]
[uxbox.common.data :as d]
[uxbox.main.constants :as c]
[uxbox.main.data.workspace :as dw]
@ -96,6 +97,7 @@
stream (->> ms/mouse-position-delta
(rx/take-until stoper))]
(st/emit! dw/start-pan)
(rx/subscribe stream
(fn [delta]
(let [vbox (.. ^js node -viewBox -baseVal)
@ -136,7 +138,8 @@
vbox
edition
tooltip
selected]} local
selected
panning]} local
viewport-ref (mf/use-ref nil)
last-position (mf/use-var nil)
@ -185,7 +188,8 @@
(st/emit! (ms/->MouseEvent :up ctrl? shift?))
(when (= 2 (.-which event))
(st/emit! ::finish-positioning)))))
(st/emit! dw/finish-pan
::finish-positioning)))))
on-pointer-down
(mf/use-callback
@ -251,7 +255,8 @@
:shift? shift?
:ctrl? ctrl?}]
(when (kbd/space? event)
(st/emit! ::finish-positioning))
(st/emit! dw/finish-pan
::finish-positioning))
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift?)))))
translate-point-to-viewport
@ -365,6 +370,15 @@
(:height vbox 0)])
:ref viewport-ref
:class (when drawing-tool "drawing")
:style {:cursor (cond
panning cur/hand
(= drawing-tool :frame) cur/create-artboard
(= drawing-tool :rect) cur/create-rectangle
(= drawing-tool :circle) cur/create-ellipse
(= drawing-tool :path) cur/pen
(= drawing-tool :curve)cur/pencil
drawing-tool cur/create-shape
:else cur/pointer-inner)}
:on-context-menu on-context-menu
:on-click on-click
:on-double-click on-double-click

View file

@ -71,3 +71,4 @@
[index {:keys [id x y width height] :as obj}]
(let [rect #js {:x x :y y :width width :height height}]
(qdt/insert index rect obj)))