diff --git a/src/uxbox/data/workspace.cljs b/src/uxbox/data/workspace.cljs index 6e9a0494b..15ce6826d 100644 --- a/src/uxbox/data/workspace.cljs +++ b/src/uxbox/data/workspace.cljs @@ -18,12 +18,11 @@ [uxbox.shapes :as sh] [uxbox.data.pages :as udp] [uxbox.data.shapes :as uds] + [uxbox.util.datetime :as dt] [uxbox.util.geom.point :as gpt] [uxbox.util.data :refer (index-of)])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Events (explicit) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Events (concrete) (defn initialize "Initialize the workspace state." @@ -93,9 +92,7 @@ (->> (into #{} xf (vals (:shapes-by-id state))) (assoc-in state [:workspace :selected])))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Events (for selected) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Events (implicit) (for selected) (defn deselect-all "Mark a shape selected for drawing in the canvas." @@ -207,3 +204,41 @@ (->> (get-in state [:workspace :selected]) (map #(uds/update-stroke-attrs % opts))))))) +;; --- Copy to Clipboard + +(defrecord CopyToClipboard [] + rs/UpdateEvent + (-apply-update [_ state] + (let [selected (get-in state [:workspace :selected]) + item {:id (random-uuid) + :created-at (dt/now) + :items selected} + clipboard (-> (:clipboard state) + (conj item))] + (assoc state :clipboard + (if (> (count clipboard) 5) + (pop clipboard) + clipboard))))) + +(defn copy-to-clipboard + "Copy selected shapes to clipboard." + [] + (CopyToClipboard.)) + +;; --- Paste from Clipboard + +(defrecord PasteFromClipboard [id] + rs/UpdateEvent + (-apply-update [_ state] + (let [page (get-in state [:workspace :page]) + selected (if (nil? id) + (first (:clipboard state)) + (->> (:clipboard state) + (filter #(= id (:id %))) + (first)))] + (stsh/duplicate-shapes state (:items selected) page)))) + +(defn paste-from-clipboard + "Copy selected shapes to clipboard." + ([] (PasteFromClipboard. nil)) + ([id] (PasteFromClipboard. id))) diff --git a/src/uxbox/state.cljs b/src/uxbox/state.cljs index 5b1739476..0959bf7da 100644 --- a/src/uxbox/state.cljs +++ b/src/uxbox/state.cljs @@ -18,6 +18,7 @@ :project-filter ""} :route nil :auth (:uxbox/auth local-storage) + :clipboard #queue [] :profile nil :workspace nil :shapes-by-id {} diff --git a/src/uxbox/state/shapes.cljs b/src/uxbox/state/shapes.cljs index 2603c64c3..6e6a944b2 100644 --- a/src/uxbox/state/shapes.cljs +++ b/src/uxbox/state/shapes.cljs @@ -3,9 +3,7 @@ (:require [uxbox.shapes :as sh] [uxbox.util.data :refer (index-of)])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Shape Creation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Shape Creation (defn assoc-shape-to-page [state shape page] @@ -48,30 +46,30 @@ (reduce #(duplicate-shape %1 %2 page group) state shapes)))) (defn duplicate-shapes - [state shapes] - (letfn [(all-toplevel? [coll] - (every? #(nil? (:group %)) coll)) - (all-same-group? [coll] - (let [group (:group (first coll))] - (every? #(= group (:group %)) coll)))] - (let [shapes (mapv #(get-in state [:shapes-by-id %]) shapes)] - (cond - (all-toplevel? shapes) - (let [page (:page (first shapes))] - (duplicate-shapes' state shapes page)) + ([state shapes] + (duplicate-shapes state shapes nil)) + ([state shapes page] + (letfn [(all-toplevel? [coll] + (every? #(nil? (:group %)) coll)) + (all-same-group? [coll] + (let [group (:group (first coll))] + (every? #(= group (:group %)) coll)))] + (let [shapes (mapv #(get-in state [:shapes-by-id %]) shapes)] + (cond + (all-toplevel? shapes) + (let [page (or page (:page (first shapes)))] + (duplicate-shapes' state shapes page)) - (all-same-group? shapes) - (let [page (:page (first shapes)) - group (:group (first shapes))] - (duplicate-shapes' state shapes page group)) + (all-same-group? shapes) + (let [page (or page (:page (first shapes))) + group (:group (first shapes))] + (duplicate-shapes' state shapes page group)) - :else - (let [page (:page (first shapes))] - (duplicate-shapes' state shapes page)))))) + :else + (let [page (or page (:page (first shapes)))] + (duplicate-shapes' state shapes page))))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Delete Shapes -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Delete Shapes (defn dissoc-from-index "A function that dissoc shape from the indexed @@ -127,9 +125,7 @@ (dissoc-from-index $ shape) (clear-empty-groups $ shape))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Shape Movements -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Shape Movements (defn- drop-at-index [index coll v] @@ -193,3 +189,26 @@ :before (drop-before state tid sid) :after (drop-after state tid sid) (throw (ex-info "Invalid data" {}))))) + +;; --- Shape Packing + +;; (defn- deep-scan-shape-ids +;; [state acc id] +;; (let [shape (get-in state [:shapes-by-id id])] +;; (if (= (:type shape) :builtin/group) +;; (reduce (partial deep-scan-shape-ids state) +;; (conj acc id) +;; (:items shape)) +;; (conj acc id)))) + +;; (defn pack-shape +;; [state id] +;; (let [ids (deep-scan-shape-ids state #{} id) +;; index (reduce (fn [acc id] +;; (let [shape (get-in state [:shapes-by-id id])] +;; (assoc acc id shape))) +;; {} ids)] +;; {:type :builtin/packed-shape +;; :index index +;; :id id})) + diff --git a/src/uxbox/ui/workspace/clipboard.cljs b/src/uxbox/ui/workspace/clipboard.cljs index 92684884b..bc458c574 100644 --- a/src/uxbox/ui/workspace/clipboard.cljs +++ b/src/uxbox/ui/workspace/clipboard.cljs @@ -7,33 +7,51 @@ (ns uxbox.ui.workspace.clipboard (:require [sablono.core :as html :refer-macros [html]] + [rum.core :as rum] + [lentes.core :as l] + [uxbox.rstore :as rs] + [uxbox.state :as st] [uxbox.ui.icons :as i] [uxbox.ui.mixins :as mx] + [uxbox.ui.lightbox :as lightbox] + [uxbox.data.workspace :as udw] [uxbox.util.dom :as dom] - [uxbox.ui.lightbox :as lightbox])) + [uxbox.util.datetime :as dt])) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Component -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; --- Lenses + +(def ^:const ^:private clipboard-l + (-> (l/key :clipboard) + (l/focus-atom st/state))) + +;; --- Clipboard Dialog Component + +(defn- on-paste + [item] + (rs/emit! (udw/paste-from-clipboard (:id item))) + (lightbox/close!)) (defn- clipboard-dialog-render [own] - (html - [:div.lightbox-body.clipboard - [:div.clipboard-list - (for [i (range 5)] - [:div.clipboard-item {:key i} - [:span.clipboard-icon i/box] - [:span (str "shape " i)]])] - [:a.close {:href "#" - :on-click #(do (dom/prevent-default %) - (lightbox/close!))} i/close]])) + (let [clipboard (rum/react clipboard-l)] + (html + [:div.lightbox-body.clipboard + [:div.clipboard-list + (for [item clipboard] + [:div.clipboard-item + {:key (str (:id item)) + :on-click (partial on-paste item)} + [:span.clipboard-icon i/box] + [:span (str "Copied (" (dt/timeago (:created-at item)) ")")]])] + [:a.close {:href "#" + :on-click #(do (dom/prevent-default %) + (lightbox/close!))} i/close]]))) (def clipboard-dialog (mx/component {:render clipboard-dialog-render :name "clipboard-dialog" - :mixins []})) + :mixins [mx/static rum/reactive]})) (defmethod lightbox/render-lightbox :clipboard [_] diff --git a/src/uxbox/ui/workspace/shortcuts.cljs b/src/uxbox/ui/workspace/shortcuts.cljs index ee6329803..5b3943325 100644 --- a/src/uxbox/ui/workspace/shortcuts.cljs +++ b/src/uxbox/ui/workspace/shortcuts.cljs @@ -10,6 +10,7 @@ (:require [goog.events :as events] [beicon.core :as rx] [uxbox.rstore :as rs] + [uxbox.ui.lightbox :as lightbox] [uxbox.data.workspace :as dw]) (:import goog.events.EventType goog.events.KeyCodes @@ -33,6 +34,9 @@ :ctrl+shift+l #(rs/emit! (dw/toggle-flag :layers)) :ctrl+r #(rs/emit! (dw/toggle-flag :ruler)) :ctrl+d #(rs/emit! (dw/duplicate-selected)) + :ctrl+c #(rs/emit! (dw/copy-to-clipboard)) + :ctrl+v #(rs/emit! (dw/paste-from-clipboard)) + :ctrl+shift+v #(lightbox/open! :clipboard) :esc #(rs/emit! (dw/deselect-all)) :backspace #(rs/emit! (dw/delete-selected)) :delete #(rs/emit! (dw/delete-selected)) diff --git a/src/uxbox/ui/workspace/sidebar/history.cljs b/src/uxbox/ui/workspace/sidebar/history.cljs index 2e9abe283..703a958b6 100644 --- a/src/uxbox/ui/workspace/sidebar/history.cljs +++ b/src/uxbox/ui/workspace/sidebar/history.cljs @@ -69,7 +69,6 @@ (on-load-more [event] (dom/prevent-default event) - (println "kaka") (let [since (:min-version history) params {:since since}] (rs/emit! (udh/fetch-page-history (:id page) params))))] diff --git a/src/uxbox/ui/workspace/sidebar/layers.cljs b/src/uxbox/ui/workspace/sidebar/layers.cljs index 9f003575a..a18bd65cf 100644 --- a/src/uxbox/ui/workspace/sidebar/layers.cljs +++ b/src/uxbox/ui/workspace/sidebar/layers.cljs @@ -101,7 +101,6 @@ :builtin/text i/text :builtin/group i/folder)) - (defn- get-hover-position [event group?] (let [target (.-currentTarget event)