mirror of
https://github.com/penpot/penpot.git
synced 2026-05-22 08:23:42 +00:00
217 lines
7.6 KiB
Clojure
217 lines
7.6 KiB
Clojure
(ns uxbox.ui.workspace.canvas
|
|
(:require [sablono.core :as html :refer-macros [html]]
|
|
[rum.core :as rum]
|
|
[beicon.core :as rx]
|
|
[cats.labs.lens :as l]
|
|
[goog.events :as events]
|
|
[uxbox.router :as r]
|
|
[uxbox.rstore :as rs]
|
|
[uxbox.state :as st]
|
|
[uxbox.xforms :as xf]
|
|
[uxbox.shapes :as sh]
|
|
[uxbox.util.lens :as ul]
|
|
[uxbox.library.icons :as _icons]
|
|
[uxbox.data.projects :as dp]
|
|
[uxbox.data.workspace :as dw]
|
|
[uxbox.ui.mixins :as mx]
|
|
[uxbox.ui.dom :as dom]
|
|
[uxbox.ui.keyboard :as kbd]
|
|
[uxbox.ui.workspace.base :as wb]
|
|
[uxbox.ui.workspace.canvas.movement]
|
|
[uxbox.ui.workspace.canvas.draw :refer (draw-area)]
|
|
[uxbox.ui.workspace.canvas.ruler :refer (ruler)]
|
|
[uxbox.ui.workspace.canvas.selection :refer (shapes-selection)]
|
|
[uxbox.ui.workspace.canvas.selrect :refer (selrect)]
|
|
[uxbox.ui.workspace.grid :refer (grid)]
|
|
[uxbox.ui.workspace.options :refer (element-opts)])
|
|
(:import goog.events.EventType))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Background
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defn background-render
|
|
[]
|
|
(html
|
|
[:rect
|
|
{:x 0 :y 0 :width "100%" :height "100%" :fill "white"}]))
|
|
|
|
(def background
|
|
(mx/component
|
|
{:render background-render
|
|
:name "background"
|
|
:mixins [mx/static]}))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Shape
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(declare shape)
|
|
|
|
(defn shape-render
|
|
[own item selected]
|
|
(let [{:keys [id x y width height group] :as item} item
|
|
selected? (contains? selected id)
|
|
local (:rum/local own)]
|
|
(letfn [(on-mouse-down [event]
|
|
(when-not (:blocked item)
|
|
(cond
|
|
(and group (:locked (sh/resolve-parent item)))
|
|
nil
|
|
|
|
(and (not selected?) (empty? selected))
|
|
(do
|
|
(dom/stop-propagation event)
|
|
(swap! local assoc :init-coords [x y])
|
|
(wb/emit-interaction! :shape/movement)
|
|
(rs/emit! (dw/select-shape id)))
|
|
|
|
(and (not selected?) (not (empty? selected)))
|
|
(do
|
|
(dom/stop-propagation event)
|
|
(swap! local assoc :init-coords [x y])
|
|
(if (.-ctrlKey event)
|
|
(rs/emit! (dw/select-shape id))
|
|
(rs/emit! (dw/deselect-all)
|
|
(dw/select-shape id))))
|
|
|
|
:else
|
|
(do
|
|
(dom/stop-propagation event)
|
|
(swap! local assoc :init-coords [x y])
|
|
(wb/emit-interaction! :shape/movement)))))
|
|
|
|
(on-mouse-up [event]
|
|
(cond
|
|
(and group (:locked (sh/resolve-parent item)))
|
|
nil
|
|
|
|
:else
|
|
(do
|
|
(dom/stop-propagation event)
|
|
(wb/emit-interaction! :nothing)
|
|
)))]
|
|
(html
|
|
[:g.shape {:class (when selected? "selected")
|
|
:on-mouse-down on-mouse-down
|
|
:on-mouse-up on-mouse-up}
|
|
(sh/-render item #(shape % selected))]))))
|
|
|
|
(def ^:static shape
|
|
(mx/component
|
|
{:render shape-render
|
|
:name "shape"
|
|
:mixins [(mx/local {}) rum/reactive]}))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Canvas
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defn- canvas-did-mount
|
|
[own]
|
|
(letfn [(on-mousemove [event page [offset-x offset-y]]
|
|
(let [x (.-clientX event)
|
|
y (.-clientY event)
|
|
event {:id (:id page)
|
|
:ctrl (kbd/ctrl? event)
|
|
:window-coords [x y]
|
|
:canvas-coords [(- x offset-x)
|
|
(- y offset-y)]}]
|
|
(rx/push! wb/mouse-b event)))]
|
|
(let [[page] (:rum/props own)
|
|
canvas (mx/get-ref-dom own (str "canvas" (:id page)))
|
|
brect (.getBoundingClientRect canvas)
|
|
brect [(.-left brect) (.-top brect)]
|
|
key (events/listen js/document EventType.MOUSEMOVE
|
|
#(on-mousemove % page brect))]
|
|
(swap! wb/bounding-rect assoc (:id page) brect)
|
|
(assoc own ::eventkey key))))
|
|
|
|
(defn- canvas-will-unmount
|
|
[own]
|
|
(let [key (::eventkey own)
|
|
[page] (:rum/props own)]
|
|
(swap! wb/bounding-rect dissoc (:id page))
|
|
(events/unlistenByKey key)
|
|
(dissoc own ::eventkey)))
|
|
|
|
(defn- canvas-transfer-state
|
|
[old-own own]
|
|
(let [key (::eventkey old-own)]
|
|
(assoc own ::eventkey key)))
|
|
|
|
(defn- canvas-render
|
|
[own {:keys [width height id] :as page}]
|
|
(let [workspace (rum/react wb/workspace-l)
|
|
shapes-by-id (rum/react wb/shapes-by-id-l)
|
|
workspace-selected (:selected workspace)
|
|
xf (comp
|
|
(map #(get shapes-by-id %))
|
|
(remove :hidden))
|
|
shapes (->> (vals shapes-by-id)
|
|
(filter #(= (:page %) id)))
|
|
shapes-selected (filter (comp workspace-selected :id) shapes)
|
|
shapes-notselected (filter (comp not workspace-selected :id) shapes)]
|
|
(html
|
|
[:svg#page-canvas.page-canvas {:x wb/document-start-x
|
|
:y wb/document-start-y
|
|
:ref (str "canvas" id)
|
|
:width width
|
|
:height height}
|
|
(background)
|
|
(grid 1)
|
|
[:svg.page-layout {}
|
|
(shapes-selection shapes-selected)
|
|
[:g.main {}
|
|
(for [item (sequence xf (:shapes page))]
|
|
(-> (shape item workspace-selected)
|
|
(rum/with-key (str (:id item)))))
|
|
(selrect)
|
|
(draw-area)]]])))
|
|
|
|
(def canvas
|
|
(mx/component
|
|
{:render canvas-render
|
|
:did-mount canvas-did-mount
|
|
:will-unmount canvas-will-unmount
|
|
:transfer-state canvas-transfer-state
|
|
:name "canvas"
|
|
:mixins [mx/static rum/reactive]}))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; Viewport Component
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
(defn viewport-render
|
|
[own]
|
|
(let [workspace (rum/react wb/workspace-l)
|
|
page (rum/react wb/page-l)
|
|
drawing? (:drawing workspace)
|
|
zoom 1]
|
|
(letfn [(on-mouse-down [event]
|
|
(dom/stop-propagation event)
|
|
(when-not (empty? (:selected workspace))
|
|
(rs/emit! (dw/deselect-all)))
|
|
(if-let [shape (:drawing workspace)]
|
|
(wb/emit-interaction! :draw/shape)
|
|
(wb/emit-interaction! :draw/selrect)))
|
|
(on-mouse-up [event]
|
|
(dom/stop-propagation event)
|
|
(wb/emit-interaction! :nothing))]
|
|
(html
|
|
[:svg.viewport {:width wb/viewport-height
|
|
:height wb/viewport-width
|
|
:class (when drawing? "drawing")
|
|
:on-mouse-down on-mouse-down
|
|
:on-mouse-up on-mouse-up}
|
|
[:g.zoom {:transform (str "scale(" zoom ", " zoom ")")}
|
|
(if page
|
|
(canvas page))
|
|
(ruler)]]))))
|
|
|
|
(def viewport
|
|
(mx/component
|
|
{:render viewport-render
|
|
:name "viewport"
|
|
:mixins [rum/reactive]}))
|