mirror of
https://github.com/penpot/penpot.git
synced 2025-04-11 06:21:30 -05:00
🎉 Copy & paste images onto the workspace
This commit is contained in:
parent
94959ffff8
commit
7bf06353ad
6 changed files with 137 additions and 102 deletions
|
@ -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
|
||||
|
|
|
@ -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)))))))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Reference in a new issue