diff --git a/src/uxbox/shapes.cljs b/src/uxbox/shapes.cljs index 1b21f4191..287a8b705 100644 --- a/src/uxbox/shapes.cljs +++ b/src/uxbox/shapes.cljs @@ -31,14 +31,6 @@ [shape & params] (:type shape)) -(defmulti -render - dispatch-by-type - :hierarchy #'+hierarchy+) - -(defmulti -render-svg - dispatch-by-type - :hierarchy #'+hierarchy+) - (defmulti -move dispatch-by-type :hierarchy #'+hierarchy+) diff --git a/src/uxbox/ui/core.cljs b/src/uxbox/ui/core.cljs new file mode 100644 index 000000000..eb8897570 --- /dev/null +++ b/src/uxbox/ui/core.cljs @@ -0,0 +1,13 @@ +(ns uxbox.ui.core + (:require [beicon.core :as rx])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Actions +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defonce actions-s (rx/bus)) + +(defn emit-action! + [type] + (rx/push! actions-s type)) + diff --git a/src/uxbox/ui/dashboard/icons.cljs b/src/uxbox/ui/dashboard/icons.cljs index 116a73045..a79090746 100644 --- a/src/uxbox/ui/dashboard/icons.cljs +++ b/src/uxbox/ui/dashboard/icons.cljs @@ -7,11 +7,11 @@ [uxbox.rstore :as rs] [uxbox.schema :as sc] [uxbox.library :as library] - [uxbox.shapes :as shapes] [uxbox.data.dashboard :as dd] [uxbox.util.lens :as ul] [uxbox.ui.icons :as i] [uxbox.ui.form :as form] + [uxbox.ui.shapes.core :as uusc] [uxbox.ui.lightbox :as lightbox] [uxbox.util.dom :as dom] [uxbox.ui.mixins :as mx])) @@ -111,7 +111,7 @@ [:div.dashboard-grid-content (for [icon (:icons coll)] [:div.grid-item.small-item.project-th {} - [:span.grid-item-image #_i/toggle (shapes/-render-svg icon nil)] + [:span.grid-item-image #_i/toggle (uusc/render-shape-svg icon nil)] [:h3 (:name icon)] #_[:div.project-th-actions [:div.project-th-icon.edit i/pencil] diff --git a/src/uxbox/ui/shapes.cljs b/src/uxbox/ui/shapes.cljs index 39729bbdd..d8496630a 100644 --- a/src/uxbox/ui/shapes.cljs +++ b/src/uxbox/ui/shapes.cljs @@ -1,115 +1,10 @@ (ns uxbox.ui.shapes "A ui related implementation for uxbox.shapes ns." - (:require [sablono.core :refer-macros [html]] - [cuerdas.core :as str] - [rum.core :as rum] - [uxbox.state :as st] - [uxbox.shapes :as sh] - [uxbox.ui.icons :as i] - [uxbox.util.math :as mth] - [uxbox.util.data :refer (remove-nil-vals)])) + (:require [uxbox.ui.shapes.core :as usc] + [uxbox.ui.shapes.icon] + [uxbox.ui.shapes.rect] + [uxbox.ui.shapes.group] + [uxbox.ui.shapes.line] + [uxbox.ui.shapes.circle])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Attribute transformations -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(def ^:static ^:private +style-attrs+ - #{:fill :opacity :stroke :stroke-opacity :stroke-width :stroke-type :rx :ry}) - -(defn- transform-stroke-type - [attrs] - (if-let [type (:stroke-type attrs)] - (let [value (case type - :dotted "1,1" - :dashed "10,10")] - (-> attrs - (assoc! :stroke-dasharray value) - (dissoc! :stroke-type))) - attrs)) - -(defn- extract-style-attrs - "Extract predefinet attrs from shapes." - [shape] - (let [attrs (select-keys shape +style-attrs+)] - (-> (transient attrs) - (transform-stroke-type) - (persistent!)))) - -(defn- make-debug-attrs - [shape] - (let [attrs (select-keys shape [:rotation :width :height :x :y]) - xf (map (fn [[x v]] - [(keyword (str "data-" (name x))) v]))] - (into {} xf attrs))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Implementation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defmethod sh/-render :builtin/icon - [{:keys [data id] :as shape} _] - (let [key (str id) - rfm (sh/-transformation shape) - attrs (merge {:id key :key key :transform (str rfm)} - (extract-style-attrs shape) - (make-debug-attrs shape))] - (html - [:g attrs data]))) - -(defmethod sh/-render :builtin/line - [{:keys [id x1 y1 x2 y2] :as shape}] - (let [key (str id) - props (select-keys shape [:x1 :x2 :y2 :y1]) - attrs (-> (extract-style-attrs shape) - (merge {:id key :key key}) - (merge props))] - (html - [:line attrs]))) - - -(defmethod sh/-render :builtin/circle - [{:keys [id] :as shape}] - (let [key (str id) - rfm (sh/-transformation shape) - props (select-keys shape [:cx :cy :rx :ry]) - attrs (-> (extract-style-attrs shape) - (merge {:id key :key key :transform (str rfm)}) - (merge props))] - (html - [:ellipse attrs]))) - -(defmethod sh/-render :builtin/rect - [{:keys [id x1 y1 x2 y2] :as shape}] - (let [key (str id) - rfm (sh/-transformation shape) - size (sh/-size shape) - props {:x x1 :y y1 :id key :key key :transform (str rfm)} - attrs (-> (extract-style-attrs shape) - (merge props size))] - (html - [:rect attrs]))) - -(defmethod sh/-render :builtin/group - [{:keys [items id dx dy rotation] :as shape} factory] - (let [key (str "group-" id) - rfm (sh/-transformation shape) - attrs (merge {:id key :key key :transform (str rfm)} - (extract-style-attrs shape) - (make-debug-attrs shape)) - shapes-by-id (get @st/state :shapes-by-id)] - (html - [:g attrs - (for [item (->> items - (map #(get shapes-by-id %)) - (remove :hidden) - (reverse))] - (-> (factory item) - (rum/with-key (str (:id item)))))]))) - -(defmethod sh/-render-svg :builtin/icon - [{:keys [data id view-box] :as shape}] - (let [key (str "icon-svg-" id) - view-box (apply str (interpose " " view-box)) - props {:view-box view-box :id key :key key}] - (html - [:svg props data]))) +(def ^:const shape usc/shape) diff --git a/src/uxbox/ui/shapes/circle.cljs b/src/uxbox/ui/shapes/circle.cljs new file mode 100644 index 000000000..9b2a82abe --- /dev/null +++ b/src/uxbox/ui/shapes/circle.cljs @@ -0,0 +1,24 @@ +(ns uxbox.ui.shapes.circle + (:require [sablono.core :refer-macros [html]] + [cuerdas.core :as str] + [rum.core :as rum] + [cats.labs.lens :as l] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.shapes :as ush] + [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.keyboard :as kbd] + [uxbox.ui.shapes.core :as uusc] + [uxbox.util.dom :as dom])) + +(defmethod uusc/render-shape :builtin/circle + [{:keys [id] :as shape}] + (let [key (str id) + rfm (ush/-transformation shape) + props (select-keys shape [:cx :cy :rx :ry]) + attrs (-> (uusc/extract-style-attrs shape) + (merge {:id key :key key :transform (str rfm)}) + (merge props))] + (html + [:ellipse attrs]))) diff --git a/src/uxbox/ui/shapes/core.cljs b/src/uxbox/ui/shapes/core.cljs new file mode 100644 index 000000000..7fa6a6f43 --- /dev/null +++ b/src/uxbox/ui/shapes/core.cljs @@ -0,0 +1,86 @@ +(ns uxbox.ui.shapes.core + (:require [cats.labs.lens :as l] + [cuerdas.core :as str] + [rum.core :as rum] + [beicon.core :as rx] + [uxbox.state :as st] + [uxbox.shapes :as sh] + [uxbox.ui.mixins :as mx])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Implementation Api +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmulti render-component + (fn [own shape] (:type shape)) + :hierarchy #'sh/+hierarchy+) + +(defmulti render-shape + sh/dispatch-by-type + :hierarchy #'sh/+hierarchy+) + +(defmulti render-shape-svg + sh/dispatch-by-type + :hierarchy #'sh/+hierarchy+) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Lenses +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:const selected-shapes-l + (as-> (l/in [:workspace :selected]) $ + (l/focus-atom $ st/state))) + +(defn- focus-shape + [id] + (as-> (l/in [:shapes-by-id id]) $ + (l/focus-atom $ st/state))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Component Api +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- shape-render + [own id] + (let [shape-l (focus-shape id) + shape (rum/react shape-l)] + (render-component own shape))) + +(def ^:const shape + (mx/component + {:render shape-render + :name "shape" + :mixins [(mx/local) mx/static rum/reactive]})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Attribute transformations +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:static ^:private +style-attrs+ + #{:fill :opacity :stroke :stroke-opacity :stroke-width :stroke-type :rx :ry}) + +(defn- transform-stroke-type + [attrs] + (if-let [type (:stroke-type attrs)] + (let [value (case type + :dotted "1,1" + :dashed "10,10")] + (-> attrs + (assoc! :stroke-dasharray value) + (dissoc! :stroke-type))) + attrs)) + +(defn- extract-style-attrs + "Extract predefinet attrs from shapes." + [shape] + (let [attrs (select-keys shape +style-attrs+)] + (-> (transient attrs) + (transform-stroke-type) + (persistent!)))) + +(defn- make-debug-attrs + [shape] + (let [attrs (select-keys shape [:rotation :width :height :x :y]) + xf (map (fn [[x v]] + [(keyword (str "data-" (name x))) v]))] + (into {} xf attrs))) diff --git a/src/uxbox/ui/shapes/group.cljs b/src/uxbox/ui/shapes/group.cljs new file mode 100644 index 000000000..0b97f3ece --- /dev/null +++ b/src/uxbox/ui/shapes/group.cljs @@ -0,0 +1,31 @@ +(ns uxbox.ui.shapes.group + (:require [sablono.core :refer-macros [html]] + [cuerdas.core :as str] + [rum.core :as rum] + [cats.labs.lens :as l] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.shapes :as ush] + [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.keyboard :as kbd] + [uxbox.ui.shapes.core :as uusc] + [uxbox.util.dom :as dom])) + +(defmethod uusc/render-shape :builtin/group + [{:keys [items id dx dy rotation] :as shape} factory] + (let [key (str "group-" id) + rfm (ush/-transformation shape) + attrs (merge {:id key :key key :transform (str rfm)} + (uusc/extract-style-attrs shape) + (uusc/make-debug-attrs shape)) + shapes-by-id (get @st/state :shapes-by-id)] + (html + [:g attrs + (for [item (->> items + (map #(get shapes-by-id %)) + (remove :hidden) + (reverse))] + (-> (factory (:id item)) + (rum/with-key (str (:id item)))))]))) + diff --git a/src/uxbox/ui/shapes/icon.cljs b/src/uxbox/ui/shapes/icon.cljs new file mode 100644 index 000000000..aa84868db --- /dev/null +++ b/src/uxbox/ui/shapes/icon.cljs @@ -0,0 +1,132 @@ +(ns uxbox.ui.shapes.icon + (:require [sablono.core :refer-macros [html]] + [cuerdas.core :as str] + [rum.core :as rum] + [cats.labs.lens :as l] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.shapes :as sh] + [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.mixins :as mx] + [uxbox.ui.keyboard :as kbd] + [uxbox.ui.shapes.core :as uusc] + [uxbox.util.dom :as dom])) + +(def ^:private ^:const selection-circle-style + {:fillOpacity "0.5" + :strokeWidth "1px" + :vectorEffect "non-scaling-stroke"}) + +(def ^:private ^:const default-selection-props + {:r 5 :style selection-circle-style + :fill "#333" + :stroke "#333"}) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Icon Component +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn on-mouse-down + [event {:keys [id group] :as shape} selected] + (let [selected? (contains? selected id)] + (when-not (:blocked shape) + (cond + (and group (:locked (sh/resolve-parent shape))) + nil + + (and (not selected?) (empty? selected)) + (do + (dom/stop-propagation event) + (uuc/emit-action! :shape/movement) + (rs/emit! (dw/select-shape id))) + + (and (not selected?) (not (empty? selected))) + (do + (dom/stop-propagation event) + (if (kbd/shift? event) + (rs/emit! (dw/select-shape id)) + (rs/emit! (dw/deselect-all) + (dw/select-shape id)))) + + :else + (do + (dom/stop-propagation event) + (uuc/emit-action! :shape/movement)))))) + +(defn on-mouse-up + [event {:keys [id group] :as shape}] + (cond + (and group (:locked (sh/resolve-parent shape))) + nil + + :else + (do + (dom/stop-propagation event) + (uuc/emit-action! :nothing)))) + +(declare handlers) + +(defmethod uusc/render-component :default ;; :builtin/icon + [own shape] + (let [{:keys [id x y width height group]} shape + selected (rum/react uusc/selected-shapes-l) + selected? (contains? selected id) + on-mouse-down #(on-mouse-down % shape selected) + on-mouse-up #(on-mouse-up % shape)] + (html + [:g.shape {:class (when selected? "selected") + :on-mouse-down on-mouse-down + :on-mouse-up on-mouse-up} + (uusc/render-shape shape #(uusc/shape %)) + (when selected? + (handlers shape))]))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Icon Handlers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- handlers-render + [own shape] + (let [{:keys [x y width height]} (sh/-outer-rect shape)] + (html + [:g.controls + [:rect {:x x :y y :width width :height height :stroke-dasharray "5,5" + :style {:stroke "#333" :fill "transparent" + :stroke-opacity "1"}}] + [:circle.top-left (merge default-selection-props + {:cx x :cy y})] + [:circle.top-right (merge default-selection-props + {:cx (+ x width) :cy y})] + [:circle.bottom-left (merge default-selection-props + {:cx x :cy (+ y height)})] + [:circle.bottom-right (merge default-selection-props + {:cx (+ x width) :cy (+ y height)})]]))) + +(def ^:const handlers + (mx/component + {:render handlers-render + :name "handlers" + :mixins [mx/static]})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Shape & Shape Svg +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmethod uusc/render-shape :builtin/icon + [{:keys [data id] :as shape} _] + (let [key (str id) + rfm (sh/-transformation shape) + attrs (merge {:id key :key key :transform (str rfm)} + (uusc/extract-style-attrs shape) + (uusc/make-debug-attrs shape))] + (html + [:g attrs data]))) + +(defmethod uusc/render-shape-svg :builtin/icon + [{:keys [data id view-box] :as shape}] + (let [key (str "icon-svg-" id) + view-box (apply str (interpose " " view-box)) + props {:view-box view-box :id key :key key}] + (html + [:svg props data]))) diff --git a/src/uxbox/ui/shapes/line.cljs b/src/uxbox/ui/shapes/line.cljs new file mode 100644 index 000000000..3d612762f --- /dev/null +++ b/src/uxbox/ui/shapes/line.cljs @@ -0,0 +1,23 @@ +(ns uxbox.ui.shapes.line + (:require [sablono.core :refer-macros [html]] + [cuerdas.core :as str] + [rum.core :as rum] + [cats.labs.lens :as l] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.shapes :as ush] + [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.keyboard :as kbd] + [uxbox.ui.shapes.core :as uusc] + [uxbox.util.dom :as dom])) + +(defmethod uusc/render-shape :builtin/line + [{:keys [id x1 y1 x2 y2] :as shape}] + (let [key (str id) + props (select-keys shape [:x1 :x2 :y2 :y1]) + attrs (-> (uusc/extract-style-attrs shape) + (merge {:id key :key key}) + (merge props))] + (html + [:line attrs]))) diff --git a/src/uxbox/ui/shapes/rect.cljs b/src/uxbox/ui/shapes/rect.cljs new file mode 100644 index 000000000..65d03f27b --- /dev/null +++ b/src/uxbox/ui/shapes/rect.cljs @@ -0,0 +1,25 @@ +(ns uxbox.ui.shapes.rect + (:require [sablono.core :refer-macros [html]] + [cuerdas.core :as str] + [rum.core :as rum] + [cats.labs.lens :as l] + [uxbox.rstore :as rs] + [uxbox.state :as st] + [uxbox.shapes :as ush] + [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.keyboard :as kbd] + [uxbox.ui.shapes.core :as uusc] + [uxbox.util.dom :as dom])) + +(defmethod uusc/render-shape :builtin/rect + [{:keys [id x1 y1 x2 y2] :as shape}] + (let [key (str id) + rfm (ush/-transformation shape) + size (ush/-size shape) + props {:x x1 :y y1 :id key :key key :transform (str rfm)} + attrs (-> (uusc/extract-style-attrs shape) + (merge props size))] + (html + [:rect attrs]))) + diff --git a/src/uxbox/ui/workspace.cljs b/src/uxbox/ui/workspace.cljs index 9656dffe6..ead9273a4 100644 --- a/src/uxbox/ui/workspace.cljs +++ b/src/uxbox/ui/workspace.cljs @@ -8,9 +8,10 @@ [uxbox.data.workspace :as dw] [uxbox.util.geom.point :as gpt] [uxbox.util.data :refer (classnames)] + [uxbox.ui.core :as uuc] [uxbox.ui.icons :as i] [uxbox.ui.mixins :as mx] - [uxbox.ui.workspace.base :as wb] + [uxbox.ui.workspace.base :as uuwb] [uxbox.ui.workspace.shortcuts :as wshortcuts] [uxbox.ui.workspace.header :refer (header)] [uxbox.ui.workspace.rules :refer (horizontal-rule vertical-rule)] @@ -27,7 +28,7 @@ (let [target (.-target event) top (.-scrollTop target) left (.-scrollLeft target)] - (rx/push! wb/scroll-b (gpt/point left top)))) + (rx/push! uuwb/scroll-b (gpt/point left top)))) (defn- on-key-down [event] @@ -39,7 +40,7 @@ (defn- workspace-render [own projectid] - (let [{:keys [flags] :as workspace} (rum/react wb/workspace-l) + (let [{:keys [flags] :as workspace} (rum/react uuwb/workspace-l) left-sidebar? (not (empty? (keep flags [:layers :sitemap :document-history]))) right-sidebar? (not (empty? (keep flags [:icons :drawtools :element-options]))) @@ -85,13 +86,13 @@ (defn- workspace-did-mount [own] (letfn [(handle-scroll-interaction [] - (let [stoper (->> wb/interactions-b + (let [stoper (->> uuc/actions-s (rx/filter #(not= % :scroll/viewport)) (rx/take 1)) local (:rum/local own) - initial @wb/mouse-viewport-a] + initial @uuwb/mouse-viewport-a] (swap! local assoc :scrolling true) - (as-> wb/mouse-viewport-s $ + (as-> uuwb/mouse-viewport-s $ (rx/take-until stoper $) (rx/subscribe $ #(on-scroll % initial) nil on-scroll-end)))) @@ -108,12 +109,12 @@ (set! (.-scrollTop el) (- cy y))))] (let [el (mx/get-ref-dom own "workspace-canvas") - sub (as-> wb/interactions-b $ + sub (as-> uuc/actions-s $ (rx/dedupe $) (rx/filter #(= :scroll/viewport %) $) (rx/on-value $ handle-scroll-interaction))] - (set! (.-scrollLeft el) wb/canvas-start-scroll-x) - (set! (.-scrollTop el) wb/canvas-start-scroll-y) + (set! (.-scrollLeft el) uuwb/canvas-start-scroll-x) + (set! (.-scrollTop el) uuwb/canvas-start-scroll-y) (assoc own ::sub sub)))) (defn- workspace-will-unmount diff --git a/src/uxbox/ui/workspace/base.cljs b/src/uxbox/ui/workspace/base.cljs index a4e5cf65a..3d50d8d6d 100644 --- a/src/uxbox/ui/workspace/base.cljs +++ b/src/uxbox/ui/workspace/base.cljs @@ -71,16 +71,6 @@ (defonce scroll-a (rx/to-atom scroll-s)) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Interactions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defonce interactions-b (rx/bus)) - -(defn emit-interaction! - [type] - (rx/push! interactions-b type)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Mouse Position Stream ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/uxbox/ui/workspace/canvas.cljs b/src/uxbox/ui/workspace/canvas.cljs index 87e23e386..25f318849 100644 --- a/src/uxbox/ui/workspace/canvas.cljs +++ b/src/uxbox/ui/workspace/canvas.cljs @@ -4,21 +4,18 @@ [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.util.geom.point :as gpt] [uxbox.util.dom :as dom] [uxbox.util.data :refer (parse-int)] + [uxbox.ui.core :as uuc] [uxbox.ui.keyboard :as kbd] - [uxbox.ui.workspace.base :as wb] + [uxbox.ui.shapes :as uus] + [uxbox.ui.mixins :as mx] + [uxbox.ui.workspace.base :as uuwb] [uxbox.ui.workspace.canvas.movement] [uxbox.ui.workspace.canvas.draw :refer (draw-area)] [uxbox.ui.workspace.canvas.ruler :refer (ruler)] @@ -43,97 +40,27 @@ :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 (kbd/shift? 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-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)] + (let [workspace (rum/react uuwb/workspace-l)] (html - [:svg.page-canvas {:x wb/canvas-start-x - :y wb/canvas-start-y + [:svg.page-canvas {:x uuwb/canvas-start-x + :y uuwb/canvas-start-y :ref (str "canvas" id) :width width :height height} (background) (grid 1) [:svg.page-layout {} - (shapes-selection shapes-selected) + #_(shapes-selection shapes-selected) [:g.main {} - (for [item (reverse (sequence xf (:shapes page)))] - (-> (shape item workspace-selected) - (rum/with-key (str (:id item))))) + (for [item (:shapes page)] + (-> (uus/shape item) + (rum/with-key (str item)))) (draw-area)]]]))) (def canvas @@ -148,8 +75,8 @@ (defn viewport-render [own] - (let [workspace (rum/react wb/workspace-l) - page (rum/react wb/page-l) + (let [workspace (rum/react uuwb/workspace-l) + page (rum/react uuwb/page-l) drawing? (:drawing workspace) zoom 1] (letfn [(on-mouse-down [event] @@ -157,14 +84,14 @@ (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))) + (uuc/emit-action! :draw/shape) + (uuc/emit-action! :draw/selrect))) (on-mouse-up [event] (dom/stop-propagation event) - (wb/emit-interaction! :nothing))] + (uuc/emit-action! :nothing))] (html - [:svg.viewport {:width wb/viewport-width - :height wb/viewport-height + [:svg.viewport {:width uuwb/viewport-width + :height uuwb/viewport-height :ref "viewport" :class (when drawing? "drawing") :on-mouse-down on-mouse-down @@ -197,11 +124,11 @@ (on-key-down [event] (when (kbd/space? event) - (wb/emit-interaction! :scroll/viewport))) + (uuc/emit-action! :scroll/viewport))) (on-key-up [event] (when (kbd/space? event) - (wb/emit-interaction! :nothing))) + (uuc/emit-action! :nothing))) (on-mousemove [event] (let [wpt (gpt/point (.-clientX event) @@ -213,7 +140,7 @@ :window-coords wpt :viewport-coords vppt :canvas-coords cvpt}] - (rx/push! wb/mouse-b event)))] + (rx/push! uuwb/mouse-b event)))] (let [key1 (events/listen js/document EventType.MOUSEMOVE on-mousemove) key2 (events/listen js/document EventType.KEYDOWN on-key-down) diff --git a/src/uxbox/ui/workspace/canvas/draw.cljs b/src/uxbox/ui/workspace/canvas/draw.cljs index 3b52854a1..7501dd8c9 100644 --- a/src/uxbox/ui/workspace/canvas/draw.cljs +++ b/src/uxbox/ui/workspace/canvas/draw.cljs @@ -5,9 +5,10 @@ [beicon.core :as rx] [cats.labs.lens :as l] [uxbox.rstore :as rs] - [uxbox.state :as st] - [uxbox.shapes :as sh] + [uxbox.shapes :as ush] [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] + [uxbox.ui.shapes.core :as uusc] [uxbox.ui.workspace.base :as wb] [uxbox.ui.mixins :as mx] [uxbox.util.geom.point :as gpt] @@ -25,8 +26,8 @@ (let [shape (rum/react +drawing-shape+) position (rum/react +drawing-position+)] (when shape - (-> (sh/-resize shape position) - (sh/-render identity))))) + (-> (ush/-resize shape position) + (uusc/render-shape identity))))) (def ^:static draw-area (mx/component @@ -44,11 +45,11 @@ (define-once :drawing-subscriptions (letfn [(init-shape [shape] (let [{:keys [x y] :as point} @wb/mouse-canvas-a - shape (sh/-initialize shape {:x1 x :y1 y :x2 x :y2 y})] + shape (ush/-initialize shape {:x1 x :y1 y :x2 x :y2 y})] (reset! +drawing-shape+ shape) (reset! +drawing-position+ (assoc point :lock false)) - (let [stoper (->> wb/interactions-b + (let [stoper (->> uuc/actions-s (rx/filter #(not= % :shape/movement)) (rx/take 1))] (as-> wb/mouse-canvas-s $ @@ -62,7 +63,7 @@ (on-complete [] (let [shape @+drawing-shape+ shpos @+drawing-position+ - shape (sh/-resize shape shpos)] + shape (ush/-resize shape shpos)] (rs/emit! (dw/add-shape shape) (dw/select-for-drawing nil)) (reset! +drawing-position+ nil) @@ -71,7 +72,7 @@ (init-icon [shape] (let [{:keys [x y]} @wb/mouse-canvas-a props {:x1 x :y1 y :x2 (+ x 100) :y2 (+ y 100)} - shape (sh/-initialize shape props)] + shape (ush/-initialize shape props)] (rs/emit! (dw/add-shape shape) (dw/select-for-drawing nil)))) (init [] @@ -82,7 +83,7 @@ :builtin/circle (init-shape shape) :builtin/line (init-shape shape))))] - (as-> wb/interactions-b $ + (as-> uuc/actions-s $ (rx/dedupe $) (rx/filter #(= :draw/shape %) $) (rx/on-value $ init)))) diff --git a/src/uxbox/ui/workspace/canvas/movement.cljs b/src/uxbox/ui/workspace/canvas/movement.cljs index 281ee234f..e8b478cc6 100644 --- a/src/uxbox/ui/workspace/canvas/movement.cljs +++ b/src/uxbox/ui/workspace/canvas/movement.cljs @@ -3,28 +3,29 @@ (:require [beicon.core :as rx] [cats.labs.lens :as l] [uxbox.rstore :as rs] - [uxbox.state :as st] - [uxbox.ui.workspace.base :as wb] + [uxbox.state :as ust] + [uxbox.ui.core :as uuc] + [uxbox.ui.workspace.base :as uuwb] [uxbox.data.workspace :as dw])) (define-once :movement-subscription (letfn [(on-value [delta] - (let [pageid (get-in @st/state [:workspace :page]) - selected (get-in @st/state [:workspace :selected]) - shapes (->> (vals @wb/shapes-by-id-l) + (let [pageid (get-in @ust/state [:workspace :page]) + selected (get-in @ust/state [:workspace :selected]) + shapes (->> (vals @uuwb/shapes-by-id-l) (filter #(= (:page %) pageid)) (filter (comp selected :id)))] (doseq [{:keys [id group]} shapes] (rs/emit! (dw/move-shape id delta))))) (init [] - (as-> wb/interactions-b $ + (as-> uuc/actions-s $ (rx/filter #(not= % :shape/movement) $) (rx/take 1 $) - (rx/take-until $ wb/mouse-delta-s) + (rx/take-until $ uuwb/mouse-delta-s) (rx/on-value $ on-value)))] - (as-> wb/interactions-b $ + (as-> uuc/actions-s $ (rx/dedupe $) (rx/filter #(= :shape/movement %) $) (rx/on-value $ init)))) diff --git a/src/uxbox/ui/workspace/canvas/selrect.cljs b/src/uxbox/ui/workspace/canvas/selrect.cljs index f6fcde64c..066ccb419 100644 --- a/src/uxbox/ui/workspace/canvas/selrect.cljs +++ b/src/uxbox/ui/workspace/canvas/selrect.cljs @@ -9,6 +9,7 @@ [uxbox.state :as st] [uxbox.shapes :as sh] [uxbox.data.workspace :as dw] + [uxbox.ui.core :as uuc] [uxbox.ui.workspace.base :as wb] [uxbox.ui.mixins :as mx] [uxbox.util.geom.point :as gpt] @@ -74,7 +75,7 @@ (reset! selrect-pos nil))) (init [] - (let [stoper (->> wb/interactions-b + (let [stoper (->> uuc/actions-s (rx/filter #(not= % :draw/selrect)) (rx/take 1)) pos @wb/mouse-viewport-a] @@ -84,7 +85,7 @@ (rx/take-until stoper $) (rx/subscribe $ on-value nil on-complete))))] - (as-> wb/interactions-b $ + (as-> uuc/actions-s $ (rx/dedupe $) (rx/filter #(= :draw/selrect %) $) (rx/on-value $ init)))) diff --git a/src/uxbox/ui/workspace/sidebar/icons.cljs b/src/uxbox/ui/workspace/sidebar/icons.cljs index ef5c7456f..73c141450 100644 --- a/src/uxbox/ui/workspace/sidebar/icons.cljs +++ b/src/uxbox/ui/workspace/sidebar/icons.cljs @@ -5,20 +5,20 @@ [uxbox.router :as r] [uxbox.rstore :as rs] [uxbox.state :as st] - [uxbox.shapes :as shapes] [uxbox.library :as library] - [uxbox.util.data :refer (read-string)] [uxbox.data.workspace :as dw] + [uxbox.ui.shapes.core :as uusc] [uxbox.ui.workspace.base :as wb] [uxbox.ui.icons :as i] [uxbox.ui.mixins :as mx] - [uxbox.util.dom :as dom])) + [uxbox.util.dom :as dom] + [uxbox.util.data :refer (read-string)])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Lenses ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(def ^:private ^:static drawing-shape +(def ^:private ^:const drawing-shape "A focused vision of the drawing property of the workspace status. This avoids rerender the whole toolbox on each workspace @@ -45,7 +45,7 @@ (defn- icon-wrapper-render [own icon] - (shapes/-render-svg icon nil)) + (uusc/render-shape-svg icon nil)) (def ^:static ^:private icon-wrapper (mx/component diff --git a/src/uxbox/ui/workspace/sidebar/layers.cljs b/src/uxbox/ui/workspace/sidebar/layers.cljs index 51eea5d9b..62e467754 100644 --- a/src/uxbox/ui/workspace/sidebar/layers.cljs +++ b/src/uxbox/ui/workspace/sidebar/layers.cljs @@ -7,10 +7,10 @@ [uxbox.router :as r] [uxbox.rstore :as rs] [uxbox.state :as st] - [uxbox.shapes :as shapes] [uxbox.library :as library] [uxbox.util.data :refer (read-string classnames)] [uxbox.data.workspace :as dw] + [uxbox.ui.shapes.core :as uusc] [uxbox.ui.workspace.base :as wb] [uxbox.ui.icons :as i] [uxbox.ui.mixins :as mx] @@ -86,7 +86,7 @@ (defn- element-icon [item] (case (:type item) - :builtin/icon (shapes/-render-svg item) + :builtin/icon (uusc/render-shape-svg item) :builtin/line i/line :builtin/circle i/circle :builtin/rect i/box