diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index f4c04d4060..9d3b38f529 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -327,6 +327,65 @@ :fill-opacity 1 :shapes []}) +(def ^:private default-color "#b1b2b5") ;; $color-gray-20 + +(def ^:private minimal-shapes + [{:type :rect + :name "Rect" + :fill-color default-color + :stroke-alignment :center} + {:type :image} + {:type :icon} + {:type :circle + :name "Circle" + :fill-color default-color} + {:type :path + :name "Path" + :stroke-style :solid + :stroke-color "#000000" + :stroke-width 2 + :stroke-alignment :center + :fill-color "#000000" + :fill-opacity 0 + :segments []} + {:type :frame + :stroke-style :none + :stroke-alignment :center + :name "Artboard"} + {:type :curve + :name "Path" + :stroke-style :solid + :stroke-color "#000000" + :stroke-width 2 + :stroke-alignment :center + :fill-color "#000000" + :fill-opacity 0 + :segments []} + {:type :text + :name "Text" + :content nil}]) + +(defn make-minimal-shape + [type] + (let [shape (d/seek #(= type (:type %)) minimal-shapes)] + (assert shape "unexpected shape type") + (assoc shape + :id (uuid/next) + :x 0 + :y 0 + :width 1 + :height 1 + :selrect {:x 0 + :x1 0 + :x2 0 + :y 0 + :y1 0 + :y2 0 + :width 1 + :height 1} + :points [] + :segments []))) + ;; --- Changes Processing Impl (defmulti process-change diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 5592e223ac..70beb5c54d 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -508,6 +508,40 @@ (when (= :text (:type attrs)) (start-edition-mode id))))))) +(defn- calculate-centered-box + [state aspect-ratio] + (if (>= aspect-ratio 1) + (let [vbox (get-in state [:workspace-local :vbox]) + width (/ (:width vbox) 2) + height (/ width aspect-ratio) + + x (+ (:x vbox) (/ width 2)) + y (+ (:y vbox) (/ (- (:height vbox) height) 2))] + + [width height x y]) + + (let [vbox (get-in state [:workspace-local :vbox]) + height (/ (:height vbox) 2) + width (* height aspect-ratio) + + y (+ (:y vbox) (/ height 2)) + x (+ (:x vbox) (/ (- (:width vbox) width) 2))] + + [width height x y]))) + +(defn create-and-add-shape + [type data aspect-ratio] + (ptk/reify ::create-and-add-shape + ptk/WatchEvent + (watch [_ state stream] + (let [[width height x y] (calculate-centered-box state aspect-ratio) + shape (-> (cp/make-minimal-shape type) + (merge data) + (geom/resize width height) + (geom/absolute-move (gpt/point x y)))] + + (rx/of (add-shape shape)))))) + @@ -1096,15 +1130,38 @@ (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}) (dws/select-shapes selected)))))) +(defn- image-uploaded + [{:keys [id name] :as image}] + (let [shape {:name name + :metadata {:width (:width image) + :height (:height image) + :uri (:uri image) + :thumb-width (:thumb-width image) + :thumb-height (:thumb-height image) + :thumb-uri (:thumb-uri image)}} + aspect-ratio (/ (:width image) (:height image))] + (st/emit! (create-and-add-shape :image shape aspect-ratio)))) + +(defn- paste-image-impl + [image] + (ptk/reify ::paste-bin-impl + ptk/WatchEvent + (watch [_ state stream] + (rx/of (dwp/upload-image image image-uploaded))))) + (def paste (ptk/reify ::paste ptk/WatchEvent (watch [_ state stream] - (->> (rx/from (wapi/read-from-clipboard)) + (->> (wapi/read-from-clipboard) (rx/map t/decode) (rx/filter #(= :copied-shapes (:type %))) (rx/map #(select-keys % [:selected :objects])) (rx/map paste-impl) + (rx/catch (partial instance? js/SyntaxError) + (fn [_] + (->> (wapi/read-image-from-clipboard) + (rx/map paste-image-impl)))) (rx/catch (fn [err] (js/console.error "Clipboard error:" err) (rx/empty))))))) diff --git a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs index 697210f75a..97874533a6 100644 --- a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs +++ b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs @@ -22,6 +22,7 @@ [uxbox.common.geom.shapes :as geom] [uxbox.common.geom.matrix :as gmt] [uxbox.common.geom.point :as gpt] + [uxbox.common.pages :as cp] [uxbox.util.geom.path :as path] [uxbox.util.i18n :as i18n :refer [t]] [uxbox.main.snap :as snap] @@ -36,100 +37,6 @@ (declare handle-finish-drawing) (declare conditional-align) -(def ^:private default-color "#b1b2b5") ;; $color-gray-20 - -(def ^:private minimal-shapes - [{:type :rect - :name "Rect" - :fill-color default-color - :stroke-alignment :center} - {:type :image} - {:type :icon} - {:type :circle - :name "Circle" - :fill-color default-color} - {:type :path - :name "Path" - :stroke-style :solid - :stroke-color "#000000" - :stroke-width 2 - :stroke-alignment :center - :fill-color "#000000" - :fill-opacity 0 - :segments []} - {:type :frame - :stroke-style :none - :stroke-alignment :center - :name "Artboard"} - {:type :curve - :name "Path" - :stroke-style :solid - :stroke-color "#000000" - :stroke-width 2 - :stroke-alignment :center - :fill-color "#000000" - :fill-opacity 0 - :segments []} - {:type :text - :name "Text" - :content nil}]) - -(defn- make-minimal-shape - [type] - (let [tool (seek #(= type (:type %)) minimal-shapes)] - (assert tool "unexpected drawing tool") - (assoc tool - :id (uuid/next) - :x 0 - :y 0 - :width 1 - :height 1 - :selrect {:x 0 - :x1 0 - :x2 0 - :y 0 - :y1 0 - :y2 0 - :width 1 - :height 1} - :points [] - :segments []))) - - -(defn- calculate-centered-box - [state aspect-ratio] - (if (>= aspect-ratio 1) - (let [vbox (get-in state [:workspace-local :vbox]) - width (/ (:width vbox) 2) - height (/ width aspect-ratio) - - x (+ (:x vbox) (/ width 2)) - y (+ (:y vbox) (/ (- (:height vbox) height) 2))] - - [width height x y]) - - (let [vbox (get-in state [:workspace-local :vbox]) - height (/ (:height vbox) 2) - width (* height aspect-ratio) - - y (+ (:y vbox) (/ height 2)) - x (+ (:x vbox) (/ (- (:width vbox) width) 2))] - - [width height x y]))) - -(defn direct-add-shape - [type data aspect-ratio] - (ptk/reify ::direct-add-shape - ptk/WatchEvent - (watch [_ state stream] - (let [[width height x y] (calculate-centered-box state aspect-ratio) - shape (-> (make-minimal-shape type) - (merge data) - (geom/resize width height) - (geom/absolute-move (gpt/point x y)))] - - (rx/of (dw/add-shape shape)))))) - (defn start-drawing [type] {:pre [(keyword? type)]} @@ -155,7 +62,7 @@ (ptk/reify ::handle-drawing ptk/UpdateEvent (update [_ state] - (let [data (make-minimal-shape type)] + (let [data (cp/make-minimal-shape type)] (update-in state [:workspace-local :drawing] merge data))) ptk/WatchEvent diff --git a/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs b/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs index d385b2164f..d0c6bde5e3 100644 --- a/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs @@ -15,7 +15,6 @@ [uxbox.main.data.workspace :as dw] [uxbox.main.store :as st] [uxbox.main.ui.components.file-uploader :refer [file-uploader]] - [uxbox.main.ui.workspace.drawarea :refer [direct-add-shape]] [uxbox.util.dom :as dom] [uxbox.util.i18n :as i18n :refer [t]] [uxbox.main.ui.icons :as i])) @@ -42,7 +41,7 @@ :thumb-height (:thumb-height image) :thumb-uri (:thumb-uri image)}} aspect-ratio (/ (:width image) (:height image))] - (st/emit! (direct-add-shape :image shape aspect-ratio)))) + (st/emit! (dw/create-and-add-shape :image shape aspect-ratio)))) on-file-selected (fn [file] diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 65c12313f8..e2ad584670 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -26,7 +26,7 @@ [uxbox.main.ui.hooks :as hooks] [uxbox.main.ui.workspace.shapes :refer [shape-wrapper frame-wrapper]] [uxbox.main.ui.workspace.shapes.interactions :refer [interactions]] - [uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing direct-add-shape]] + [uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing]] [uxbox.main.ui.workspace.selection :refer [selection-handlers]] [uxbox.main.ui.workspace.presence :as presence] [uxbox.main.ui.workspace.snap-points :refer [snap-points]] @@ -328,7 +328,7 @@ :thumb-height (:thumb-height image) :thumb-uri (:thumb-uri image)}} aspect-ratio (/ (:width image) (:height image))] - (st/emit! (direct-add-shape :image shape aspect-ratio)))) + (st/emit! (dw/create-and-add-shape :image shape aspect-ratio)))) on-drop (fn [event] diff --git a/frontend/src/uxbox/util/webapi.cljs b/frontend/src/uxbox/util/webapi.cljs index 825a68e79d..137a18d760 100644 --- a/frontend/src/uxbox/util/webapi.cljs +++ b/frontend/src/uxbox/util/webapi.cljs @@ -13,6 +13,7 @@ [promesa.core :as p] [beicon.core :as rx] [cuerdas.core :as str] + [uxbox.common.data :as d] [uxbox.util.transit :as t])) (defn read-file-as-text @@ -79,8 +80,20 @@ (defn- read-from-clipboard [] (let [cboard (unchecked-get js/navigator "clipboard")] - (-> (.readText cboard) - (p/then identity)))) + (rx/from (.readText cboard)))) + +(defn- read-image-from-clipboard + [] + (let [cboard (unchecked-get js/navigator "clipboard") + read-item (fn [item] + (let [img-type (->> (.-types item) + (d/seek #(str/starts-with? % "image/")))] + (if img-type + (rx/from (.getType item img-type)) + (rx/empty))))] + (->> (rx/from (.read cboard)) ;; Get a stream of item lists + (rx/mapcat identity) ;; Convert each item into an emission + (rx/switch-map read-item)))) (defn request-fullscreen [el]