0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-10 00:40:30 -05:00

Add clipboard management.

This commit is contained in:
Andrey Antukh 2016-04-02 13:00:41 +03:00
parent 4519d6e508
commit b5155eebcf
7 changed files with 125 additions and 50 deletions

View file

@ -18,12 +18,11 @@
[uxbox.shapes :as sh] [uxbox.shapes :as sh]
[uxbox.data.pages :as udp] [uxbox.data.pages :as udp]
[uxbox.data.shapes :as uds] [uxbox.data.shapes :as uds]
[uxbox.util.datetime :as dt]
[uxbox.util.geom.point :as gpt] [uxbox.util.geom.point :as gpt]
[uxbox.util.data :refer (index-of)])) [uxbox.util.data :refer (index-of)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Events (concrete)
;; Events (explicit)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn initialize (defn initialize
"Initialize the workspace state." "Initialize the workspace state."
@ -93,9 +92,7 @@
(->> (into #{} xf (vals (:shapes-by-id state))) (->> (into #{} xf (vals (:shapes-by-id state)))
(assoc-in state [:workspace :selected])))))) (assoc-in state [:workspace :selected]))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Events (implicit) (for selected)
;; Events (for selected)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn deselect-all (defn deselect-all
"Mark a shape selected for drawing in the canvas." "Mark a shape selected for drawing in the canvas."
@ -207,3 +204,41 @@
(->> (get-in state [:workspace :selected]) (->> (get-in state [:workspace :selected])
(map #(uds/update-stroke-attrs % opts))))))) (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)))

View file

@ -18,6 +18,7 @@
:project-filter ""} :project-filter ""}
:route nil :route nil
:auth (:uxbox/auth local-storage) :auth (:uxbox/auth local-storage)
:clipboard #queue []
:profile nil :profile nil
:workspace nil :workspace nil
:shapes-by-id {} :shapes-by-id {}

View file

@ -3,9 +3,7 @@
(:require [uxbox.shapes :as sh] (:require [uxbox.shapes :as sh]
[uxbox.util.data :refer (index-of)])) [uxbox.util.data :refer (index-of)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Shape Creation
;; Shape Creation
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn assoc-shape-to-page (defn assoc-shape-to-page
[state shape page] [state shape page]
@ -48,30 +46,30 @@
(reduce #(duplicate-shape %1 %2 page group) state shapes)))) (reduce #(duplicate-shape %1 %2 page group) state shapes))))
(defn duplicate-shapes (defn duplicate-shapes
[state shapes] ([state shapes]
(letfn [(all-toplevel? [coll] (duplicate-shapes state shapes nil))
(every? #(nil? (:group %)) coll)) ([state shapes page]
(all-same-group? [coll] (letfn [(all-toplevel? [coll]
(let [group (:group (first coll))] (every? #(nil? (:group %)) coll))
(every? #(= group (:group %)) coll)))] (all-same-group? [coll]
(let [shapes (mapv #(get-in state [:shapes-by-id %]) shapes)] (let [group (:group (first coll))]
(cond (every? #(= group (:group %)) coll)))]
(all-toplevel? shapes) (let [shapes (mapv #(get-in state [:shapes-by-id %]) shapes)]
(let [page (:page (first shapes))] (cond
(duplicate-shapes' state shapes page)) (all-toplevel? shapes)
(let [page (or page (:page (first shapes)))]
(duplicate-shapes' state shapes page))
(all-same-group? shapes) (all-same-group? shapes)
(let [page (:page (first shapes)) (let [page (or page (:page (first shapes)))
group (:group (first shapes))] group (:group (first shapes))]
(duplicate-shapes' state shapes page group)) (duplicate-shapes' state shapes page group))
:else :else
(let [page (:page (first shapes))] (let [page (or page (:page (first shapes)))]
(duplicate-shapes' state shapes page)))))) (duplicate-shapes' state shapes page)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Delete Shapes
;; Delete Shapes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn dissoc-from-index (defn dissoc-from-index
"A function that dissoc shape from the indexed "A function that dissoc shape from the indexed
@ -127,9 +125,7 @@
(dissoc-from-index $ shape) (dissoc-from-index $ shape)
(clear-empty-groups $ shape))) (clear-empty-groups $ shape)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Shape Movements
;; Shape Movements
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- drop-at-index (defn- drop-at-index
[index coll v] [index coll v]
@ -193,3 +189,26 @@
:before (drop-before state tid sid) :before (drop-before state tid sid)
:after (drop-after state tid sid) :after (drop-after state tid sid)
(throw (ex-info "Invalid data" {}))))) (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}))

View file

@ -7,33 +7,51 @@
(ns uxbox.ui.workspace.clipboard (ns uxbox.ui.workspace.clipboard
(:require [sablono.core :as html :refer-macros [html]] (: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.icons :as i]
[uxbox.ui.mixins :as mx] [uxbox.ui.mixins :as mx]
[uxbox.ui.lightbox :as lightbox]
[uxbox.data.workspace :as udw]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.ui.lightbox :as lightbox])) [uxbox.util.datetime :as dt]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; --- Lenses
;; Component
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (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 (defn- clipboard-dialog-render
[own] [own]
(html (let [clipboard (rum/react clipboard-l)]
[:div.lightbox-body.clipboard (html
[:div.clipboard-list [:div.lightbox-body.clipboard
(for [i (range 5)] [:div.clipboard-list
[:div.clipboard-item {:key i} (for [item clipboard]
[:span.clipboard-icon i/box] [:div.clipboard-item
[:span (str "shape " i)]])] {:key (str (:id item))
[:a.close {:href "#" :on-click (partial on-paste item)}
:on-click #(do (dom/prevent-default %) [:span.clipboard-icon i/box]
(lightbox/close!))} i/close]])) [:span (str "Copied (" (dt/timeago (:created-at item)) ")")]])]
[:a.close {:href "#"
:on-click #(do (dom/prevent-default %)
(lightbox/close!))} i/close]])))
(def clipboard-dialog (def clipboard-dialog
(mx/component (mx/component
{:render clipboard-dialog-render {:render clipboard-dialog-render
:name "clipboard-dialog" :name "clipboard-dialog"
:mixins []})) :mixins [mx/static rum/reactive]}))
(defmethod lightbox/render-lightbox :clipboard (defmethod lightbox/render-lightbox :clipboard
[_] [_]

View file

@ -10,6 +10,7 @@
(:require [goog.events :as events] (:require [goog.events :as events]
[beicon.core :as rx] [beicon.core :as rx]
[uxbox.rstore :as rs] [uxbox.rstore :as rs]
[uxbox.ui.lightbox :as lightbox]
[uxbox.data.workspace :as dw]) [uxbox.data.workspace :as dw])
(:import goog.events.EventType (:import goog.events.EventType
goog.events.KeyCodes goog.events.KeyCodes
@ -33,6 +34,9 @@
:ctrl+shift+l #(rs/emit! (dw/toggle-flag :layers)) :ctrl+shift+l #(rs/emit! (dw/toggle-flag :layers))
:ctrl+r #(rs/emit! (dw/toggle-flag :ruler)) :ctrl+r #(rs/emit! (dw/toggle-flag :ruler))
:ctrl+d #(rs/emit! (dw/duplicate-selected)) :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)) :esc #(rs/emit! (dw/deselect-all))
:backspace #(rs/emit! (dw/delete-selected)) :backspace #(rs/emit! (dw/delete-selected))
:delete #(rs/emit! (dw/delete-selected)) :delete #(rs/emit! (dw/delete-selected))

View file

@ -69,7 +69,6 @@
(on-load-more [event] (on-load-more [event]
(dom/prevent-default event) (dom/prevent-default event)
(println "kaka")
(let [since (:min-version history) (let [since (:min-version history)
params {:since since}] params {:since since}]
(rs/emit! (udh/fetch-page-history (:id page) params))))] (rs/emit! (udh/fetch-page-history (:id page) params))))]

View file

@ -101,7 +101,6 @@
:builtin/text i/text :builtin/text i/text
:builtin/group i/folder)) :builtin/group i/folder))
(defn- get-hover-position (defn- get-hover-position
[event group?] [event group?]
(let [target (.-currentTarget event) (let [target (.-currentTarget event)