mirror of
https://github.com/penpot/penpot.git
synced 2026-09-18 18:06:14 +00:00
Merge pull request #148 from uxbox/us/182/workspace-copy-paste
Copy&Paste preview
This commit is contained in:
commit
0c4c50270c
@ -215,11 +215,14 @@
|
|||||||
|
|
||||||
(defmethod process-change :del-obj
|
(defmethod process-change :del-obj
|
||||||
[data {:keys [id] :as change}]
|
[data {:keys [id] :as change}]
|
||||||
(when-let [{:keys [frame-id] :as obj} (get-in data [:objects id])]
|
(when-let [{:keys [frame-id shapes] :as obj} (get-in data [:objects id])]
|
||||||
(-> data
|
(let [data (update data :objects dissoc id)]
|
||||||
(update :objects dissoc id)
|
(cond-> data
|
||||||
(update-in [:objects frame-id :shapes]
|
(contains? (:objects data) frame-id)
|
||||||
(fn [s] (filterv #(not= % id) s))))))
|
(update-in [:objects frame-id :shapes] (fn [s] (filterv #(not= % id) s)))
|
||||||
|
|
||||||
|
(seq shapes) ; Recursive delete all dependend objects
|
||||||
|
(as-> $ (reduce #(process-change %1 {:type :del-obj :id %2}) $ shapes))))))
|
||||||
|
|
||||||
(defmethod process-operation :set
|
(defmethod process-operation :set
|
||||||
[shape op]
|
[shape op]
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns ^:figwheel-hooks uxbox.main
|
(ns ^:figwheel-hooks uxbox.main
|
||||||
(:require
|
(:require
|
||||||
|
|||||||
@ -11,6 +11,8 @@
|
|||||||
(:require
|
(:require
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
|
[goog.object :as gobj]
|
||||||
|
[goog.events :as events]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.common.data :as d]
|
[uxbox.common.data :as d]
|
||||||
@ -36,7 +38,11 @@
|
|||||||
[uxbox.util.time :as dt]
|
[uxbox.util.time :as dt]
|
||||||
[uxbox.util.transit :as t]
|
[uxbox.util.transit :as t]
|
||||||
[uxbox.util.uuid :as uuid]
|
[uxbox.util.uuid :as uuid]
|
||||||
[vendor.randomcolor]))
|
[uxbox.util.webapi :as wapi]
|
||||||
|
[vendor.randomcolor])
|
||||||
|
(:import goog.events.EventType
|
||||||
|
goog.events.KeyCodes
|
||||||
|
goog.ui.KeyboardShortcutHandler))
|
||||||
|
|
||||||
;; TODO: temporal workaround
|
;; TODO: temporal workaround
|
||||||
(def clear-ruler nil)
|
(def clear-ruler nil)
|
||||||
@ -358,6 +364,63 @@
|
|||||||
(let [local (:workspace-local state)]
|
(let [local (:workspace-local state)]
|
||||||
(assoc-in state [:workspace-cache page-id] local)))))
|
(assoc-in state [:workspace-cache page-id] local)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Data Persistence
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(declare persist-changes)
|
||||||
|
(declare diff-and-commit-changes)
|
||||||
|
|
||||||
|
(defn initialize-page-persistence
|
||||||
|
[page-id]
|
||||||
|
(ptk/reify ::initialize-persistence
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc state ::page-id page-id))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [stoper (rx/filter #(or (ptk/type? ::finalize %)
|
||||||
|
(ptk/type? ::initialize-page %))
|
||||||
|
stream)
|
||||||
|
notifier (->> stream
|
||||||
|
(rx/filter (ptk/type? ::commit-changes))
|
||||||
|
(rx/debounce 2000)
|
||||||
|
(rx/merge stoper))]
|
||||||
|
(rx/merge
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? ::commit-changes))
|
||||||
|
(rx/map deref)
|
||||||
|
(rx/buffer-until notifier)
|
||||||
|
(rx/map vec)
|
||||||
|
(rx/filter (complement empty?))
|
||||||
|
(rx/map #(persist-changes page-id %))
|
||||||
|
(rx/take-until (rx/delay 100 stoper)))
|
||||||
|
(->> stream
|
||||||
|
(rx/filter #(satisfies? IBatchedChange %))
|
||||||
|
(rx/debounce 200)
|
||||||
|
(rx/map (fn [_] (diff-and-commit-changes page-id)))
|
||||||
|
(rx/take-until stoper)))))))
|
||||||
|
|
||||||
|
(defn persist-changes
|
||||||
|
[page-id changes]
|
||||||
|
(ptk/reify ::persist-changes
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [session-id (:session-id state)
|
||||||
|
page (get-in state [:pages page-id])
|
||||||
|
changes (->> changes
|
||||||
|
(mapcat identity)
|
||||||
|
(map #(assoc % :session-id session-id))
|
||||||
|
(vec))
|
||||||
|
params {:id (:id page)
|
||||||
|
:revn (:revn page)
|
||||||
|
:changes changes}]
|
||||||
|
(->> (rp/mutation :update-page params)
|
||||||
|
(rx/map shapes-changes-commited))))))
|
||||||
|
|
||||||
|
|
||||||
(defn- generate-operations
|
(defn- generate-operations
|
||||||
[ma mb]
|
[ma mb]
|
||||||
(let [ma-keys (set (keys ma))
|
(let [ma-keys (set (keys ma))
|
||||||
@ -410,60 +473,6 @@
|
|||||||
(when-not (empty? changes)
|
(when-not (empty? changes)
|
||||||
(rx/of (commit-changes changes undo-changes)))))))
|
(rx/of (commit-changes changes undo-changes)))))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Data Persistence
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(declare persist-changes)
|
|
||||||
|
|
||||||
(defn initialize-page-persistence
|
|
||||||
[page-id]
|
|
||||||
(ptk/reify ::initialize-persistence
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc state ::page-id page-id))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [stoper (rx/filter #(or (ptk/type? ::finalize %)
|
|
||||||
(ptk/type? ::initialize-page %))
|
|
||||||
stream)
|
|
||||||
notifier (->> stream
|
|
||||||
(rx/filter (ptk/type? ::commit-changes))
|
|
||||||
(rx/debounce 2000)
|
|
||||||
(rx/merge stoper))]
|
|
||||||
(rx/merge
|
|
||||||
(->> stream
|
|
||||||
(rx/filter (ptk/type? ::commit-changes))
|
|
||||||
(rx/map deref)
|
|
||||||
(rx/buffer-until notifier)
|
|
||||||
(rx/map vec)
|
|
||||||
(rx/filter (complement empty?))
|
|
||||||
(rx/map #(persist-changes page-id %))
|
|
||||||
(rx/take-until (rx/delay 100 stoper)))
|
|
||||||
(->> stream
|
|
||||||
(rx/filter #(satisfies? IBatchedChange %))
|
|
||||||
(rx/debounce 200)
|
|
||||||
(rx/map (fn [_] (diff-and-commit-changes page-id)))
|
|
||||||
(rx/take-until stoper)))))))
|
|
||||||
|
|
||||||
(defn persist-changes
|
|
||||||
[page-id changes]
|
|
||||||
(ptk/reify ::persist-changes
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [session-id (:session-id state)
|
|
||||||
page (get-in state [:pages page-id])
|
|
||||||
changes (->> changes
|
|
||||||
(mapcat identity)
|
|
||||||
(map #(assoc % :session-id session-id))
|
|
||||||
(vec))
|
|
||||||
params {:id (:id page)
|
|
||||||
:revn (:revn page)
|
|
||||||
:changes changes}]
|
|
||||||
(->> (rp/mutation :update-page params)
|
|
||||||
(rx/map shapes-changes-commited))))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Data Fetching & Uploading
|
;; Data Fetching & Uploading
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -896,7 +905,7 @@
|
|||||||
in the current workspace page."
|
in the current workspace page."
|
||||||
[state]
|
[state]
|
||||||
(let [page-id (::page-id state)
|
(let [page-id (::page-id state)
|
||||||
objects (get-in state [:workspace-page page-id :objects])]
|
objects (get-in state [:workspace-data page-id :objects])]
|
||||||
(into #{} (map :name) (vals objects))))
|
(into #{} (map :name) (vals objects))))
|
||||||
|
|
||||||
(defn impl-generate-unique-name
|
(defn impl-generate-unique-name
|
||||||
@ -1043,14 +1052,12 @@
|
|||||||
{:type :add-obj
|
{:type :add-obj
|
||||||
:id (:id obj)
|
:id (:id obj)
|
||||||
:frame-id frame-id
|
:frame-id frame-id
|
||||||
:obj (assoc obj :frame-id frame-id)
|
:obj (assoc obj :frame-id frame-id)}))
|
||||||
:session-id (:session-id state)}))
|
|
||||||
(:shapes frame))
|
(:shapes frame))
|
||||||
|
|
||||||
uchanges (mapv (fn [rch]
|
uchanges (mapv (fn [rch]
|
||||||
{:type :del-obj
|
{:type :del-obj
|
||||||
:id (:id rch)
|
:id (:id rch)})
|
||||||
:session-id (:session-id state)})
|
|
||||||
rchanges)
|
rchanges)
|
||||||
|
|
||||||
shapes (mapv :id rchanges)
|
shapes (mapv :id rchanges)
|
||||||
@ -1068,7 +1075,7 @@
|
|||||||
:id frame-id
|
:id frame-id
|
||||||
:session-id (:session-id state)}]
|
:session-id (:session-id state)}]
|
||||||
(rx/of (commit-changes (d/concat [rchange] rchanges)
|
(rx/of (commit-changes (d/concat [rchange] rchanges)
|
||||||
(d/concat [uchange] uchanges)
|
(d/concat [] uchanges [uchange])
|
||||||
{:commit-local? true}))))))
|
{:commit-local? true}))))))
|
||||||
|
|
||||||
|
|
||||||
@ -1096,7 +1103,6 @@
|
|||||||
(rx/empty))))))
|
(rx/empty))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Toggle shape's selection status (selected or deselected)
|
;; --- Toggle shape's selection status (selected or deselected)
|
||||||
|
|
||||||
(defn select-shape
|
(defn select-shape
|
||||||
@ -1979,6 +1985,95 @@
|
|||||||
(assoc-in state [:workspace-local :context-menu] nil))))
|
(assoc-in state [:workspace-local :context-menu] nil))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Clipboard
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(def copy-selected
|
||||||
|
(letfn [(prepare-selected [state selected]
|
||||||
|
(let [data (reduce #(prepare %1 state %2) {} selected)]
|
||||||
|
{:type :copied-shapes
|
||||||
|
:data (assoc data :selected selected)}))
|
||||||
|
|
||||||
|
(prepare [result state id]
|
||||||
|
(let [page-id (::page-id state)
|
||||||
|
objects (get-in state [:workspace-data page-id :objects])
|
||||||
|
object (get objects id)]
|
||||||
|
(cond-> (assoc-in result [:objects id] object)
|
||||||
|
(= :frame (:type object))
|
||||||
|
(as-> $ (reduce #(prepare %1 state %2) $ (:shapes object))))))
|
||||||
|
|
||||||
|
(on-copy-error [error]
|
||||||
|
(js/console.error "Clipboard blocked:" error)
|
||||||
|
(rx/empty))]
|
||||||
|
|
||||||
|
(ptk/reify ::copy-selected
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [selected (get-in state [:workspace-local :selected])
|
||||||
|
cdata (prepare-selected state selected)]
|
||||||
|
(->> (rx/from (wapi/write-to-clipboard cdata))
|
||||||
|
(rx/catch on-copy-error)
|
||||||
|
(rx/ignore)))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- paste-impl
|
||||||
|
[{:keys [selected objects] :as data}]
|
||||||
|
(letfn [(prepare-change [id]
|
||||||
|
(let [obj (get objects id)]
|
||||||
|
;; (prn "prepare-change" id obj)
|
||||||
|
(if (= :frame (:type obj))
|
||||||
|
(prepare-frame-change obj)
|
||||||
|
(prepare-shape-change obj uuid/zero))))
|
||||||
|
|
||||||
|
(prepare-shape-change [obj frame-id]
|
||||||
|
(let [id (uuid/next)]
|
||||||
|
{:type :add-obj
|
||||||
|
:id id
|
||||||
|
:frame-id frame-id
|
||||||
|
:obj (assoc obj :id id :frame-id frame-id)}))
|
||||||
|
|
||||||
|
(prepare-frame-change [obj]
|
||||||
|
(let [frame-id (uuid/next)
|
||||||
|
sch (->> (map #(get objects %) (:shapes obj))
|
||||||
|
(map #(prepare-shape-change % frame-id)))
|
||||||
|
fch {:type :add-obj
|
||||||
|
:id frame-id
|
||||||
|
:frame-id uuid/zero
|
||||||
|
:obj (-> obj
|
||||||
|
(assoc :id frame-id)
|
||||||
|
(assoc :frame-id uuid/zero)
|
||||||
|
(assoc :shapes (mapv :id sch)))}]
|
||||||
|
(d/concat [fch] sch)))]
|
||||||
|
|
||||||
|
(ptk/reify ::paste-impl
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [rchanges (->> (map prepare-change selected)
|
||||||
|
(flatten))
|
||||||
|
uchanges (map (fn [ch]
|
||||||
|
{:type :del-obj
|
||||||
|
:id (:id ch)})
|
||||||
|
rchanges)]
|
||||||
|
(cljs.pprint/pprint rchanges)
|
||||||
|
(rx/of (commit-changes (vec rchanges)
|
||||||
|
(vec (reverse uchanges))
|
||||||
|
{:commit-local? true})))))))
|
||||||
|
|
||||||
|
(def paste
|
||||||
|
(ptk/reify ::paste
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(->> (rx/from (wapi/read-from-clipboard))
|
||||||
|
(rx/filter #(= :copied-shapes (:type %)))
|
||||||
|
(rx/pr-log "pasting:")
|
||||||
|
(rx/map :data)
|
||||||
|
(rx/map paste-impl)
|
||||||
|
(rx/catch (fn [err]
|
||||||
|
(js/console.error "Clipboard blocked:" err)
|
||||||
|
(rx/empty)))))))
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Page Changes Reactions
|
;; Page Changes Reactions
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
@ -1998,3 +2093,63 @@
|
|||||||
pages (vec (concat before [id] after))]
|
pages (vec (concat before [id] after))]
|
||||||
(assoc-in state [:projects (:project-id page) :pages] pages)))))
|
(assoc-in state [:projects (:project-id page) :pages] pages)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Shortcuts
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(def shortcuts
|
||||||
|
{"ctrl+shift+m" #(rx/of (toggle-layout-flag :sitemap))
|
||||||
|
"ctrl+shift+f" #(rx/of (toggle-layout-flag :drawtools))
|
||||||
|
"ctrl+shift+i" #(rx/of (toggle-layout-flag :icons))
|
||||||
|
"ctrl+shift+l" #(rx/of (toggle-layout-flag :layers))
|
||||||
|
"ctrl+0" #(rx/of (reset-zoom))
|
||||||
|
"ctrl+d" #(rx/of duplicate-selected)
|
||||||
|
"ctrl+z" #(rx/of undo)
|
||||||
|
"ctrl+shift+z" #(rx/of redo)
|
||||||
|
"ctrl+y" #(rx/of redo)
|
||||||
|
"ctrl+q" #(rx/of reinitialize-undo)
|
||||||
|
"ctrl+b" #(rx/of (select-for-drawing :rect))
|
||||||
|
"ctrl+e" #(rx/of (select-for-drawing :circle))
|
||||||
|
"ctrl+t" #(rx/of (select-for-drawing :text))
|
||||||
|
"ctrl+c" #(rx/of copy-selected)
|
||||||
|
"ctrl+v" #(rx/of paste)
|
||||||
|
"esc" #(rx/of :interrupt deselect-all)
|
||||||
|
"delete" #(rx/of delete-selected)
|
||||||
|
"ctrl+up" #(rx/of (vertical-order-selected :up))
|
||||||
|
"ctrl+down" #(rx/of (vertical-order-selected :down))
|
||||||
|
"ctrl+shift+up" #(rx/of (vertical-order-selected :top))
|
||||||
|
"ctrl+shift+down" #(rx/of (vertical-order-selected :bottom))
|
||||||
|
"shift+up" #(rx/of (move-selected :up true))
|
||||||
|
"shift+down" #(rx/of (move-selected :down true))
|
||||||
|
"shift+right" #(rx/of (move-selected :right true))
|
||||||
|
"shift+left" #(rx/of (move-selected :left true))
|
||||||
|
"up" #(rx/of (move-selected :up false))
|
||||||
|
"down" #(rx/of (move-selected :down false))
|
||||||
|
"right" #(rx/of (move-selected :right false))
|
||||||
|
"left" #(rx/of (move-selected :left false))})
|
||||||
|
|
||||||
|
(def initialize-shortcuts
|
||||||
|
(letfn [(initialize [sink]
|
||||||
|
(let [handler (KeyboardShortcutHandler. js/document)]
|
||||||
|
|
||||||
|
;; Register shortcuts.
|
||||||
|
(run! #(.registerShortcut handler % %) (keys shortcuts))
|
||||||
|
|
||||||
|
;; Initialize shortcut listener.
|
||||||
|
(let [event KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED
|
||||||
|
callback #(sink (gobj/get % "identifier"))
|
||||||
|
key (events/listen handler event callback)]
|
||||||
|
(fn []
|
||||||
|
(events/unlistenByKey key)
|
||||||
|
(.clearKeyListener handler)))))]
|
||||||
|
(ptk/reify ::initialize-shortcuts
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [stoper (rx/filter #(= ::finalize-shortcuts %) stream)]
|
||||||
|
(->> (rx/create initialize)
|
||||||
|
(rx/pr-log "[debug]: shortcut:")
|
||||||
|
(rx/map #(get shortcuts %))
|
||||||
|
(rx/filter fn?)
|
||||||
|
(rx/merge-map (fn [f] (f)))
|
||||||
|
(rx/take-until stoper)))))))
|
||||||
|
|||||||
@ -133,7 +133,6 @@
|
|||||||
|
|
||||||
translate #(translate-to-frame % ds-modifier (gpt/point (- x) (- y)))
|
translate #(translate-to-frame % ds-modifier (gpt/point (- x) (- y)))
|
||||||
]
|
]
|
||||||
|
|
||||||
[:svg {:x x :y y :width width :height height}
|
[:svg {:x x :y y :width width :height height}
|
||||||
[:& "rect" props]
|
[:& "rect" props]
|
||||||
(for [item (reverse childs)]
|
(for [item (reverse childs)]
|
||||||
|
|||||||
@ -26,7 +26,6 @@
|
|||||||
[uxbox.main.ui.workspace.header :refer [header]]
|
[uxbox.main.ui.workspace.header :refer [header]]
|
||||||
[uxbox.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
|
[uxbox.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
|
||||||
[uxbox.main.ui.workspace.scroll :as scroll]
|
[uxbox.main.ui.workspace.scroll :as scroll]
|
||||||
[uxbox.main.ui.workspace.shortcuts :as shortcuts]
|
|
||||||
[uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
|
[uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
|
||||||
[uxbox.main.ui.workspace.sidebar.history :refer [history-dialog]]
|
[uxbox.main.ui.workspace.sidebar.history :refer [history-dialog]]
|
||||||
[uxbox.main.ui.workspace.left-toolbar :refer [left-toolbar]]
|
[uxbox.main.ui.workspace.left-toolbar :refer [left-toolbar]]
|
||||||
@ -119,10 +118,11 @@
|
|||||||
{:fn #(st/emit! dw/initialize-layout)})
|
{:fn #(st/emit! dw/initialize-layout)})
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
{:deps (mf/deps file-id page-id)
|
{:deps (mf/deps file-id)
|
||||||
:fn (fn []
|
:fn (fn []
|
||||||
(let [sub (shortcuts/init)]
|
(st/emit! dw/initialize-shortcuts)
|
||||||
#(rx/cancel! sub)))})
|
#(st/emit! ::dw/finalize-shortcuts))})
|
||||||
|
|
||||||
(let [file (mf/deref refs/workspace-file)
|
(let [file (mf/deref refs/workspace-file)
|
||||||
page (mf/deref refs/workspace-page)
|
page (mf/deref refs/workspace-page)
|
||||||
layout (mf/deref refs/workspace-layout)]
|
layout (mf/deref refs/workspace-layout)]
|
||||||
|
|||||||
@ -1,81 +0,0 @@
|
|||||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
|
||||||
|
|
||||||
(ns uxbox.main.ui.workspace.shortcuts
|
|
||||||
(:require [goog.events :as events]
|
|
||||||
[beicon.core :as rx]
|
|
||||||
[potok.core :as ptk]
|
|
||||||
[uxbox.main.store :as st]
|
|
||||||
[uxbox.main.data.lightbox :as dl]
|
|
||||||
[uxbox.main.data.workspace :as dw])
|
|
||||||
(:import goog.events.EventType
|
|
||||||
goog.events.KeyCodes
|
|
||||||
goog.ui.KeyboardShortcutHandler))
|
|
||||||
|
|
||||||
(declare move-selected)
|
|
||||||
|
|
||||||
;; --- Shortcuts
|
|
||||||
|
|
||||||
(defonce +shortcuts+
|
|
||||||
{
|
|
||||||
;; :shift+g #(st/emit! (dw/toggle-flag :grid))
|
|
||||||
:ctrl+shift+m #(st/emit! (dw/toggle-layout-flag :sitemap))
|
|
||||||
:ctrl+shift+f #(st/emit! (dw/toggle-layout-flag :drawtools))
|
|
||||||
:ctrl+shift+i #(st/emit! (dw/toggle-layout-flag :icons))
|
|
||||||
:ctrl+shift+l #(st/emit! (dw/toggle-layout-flag :layers))
|
|
||||||
:ctrl+0 #(st/emit! (dw/reset-zoom))
|
|
||||||
;; :ctrl+r #(st/emit! (dw/toggle-flag :ruler))
|
|
||||||
:ctrl+d #(st/emit! dw/duplicate-selected)
|
|
||||||
:ctrl+z #(st/emit! dw/undo)
|
|
||||||
:ctrl+shift+z #(st/emit! dw/redo)
|
|
||||||
:ctrl+y #(st/emit! dw/redo)
|
|
||||||
:ctrl+q #(st/emit! dw/reinitialize-undo)
|
|
||||||
:ctrl+b #(st/emit! (dw/select-for-drawing :rect))
|
|
||||||
:ctrl+e #(st/emit! (dw/select-for-drawing :circle))
|
|
||||||
:ctrl+t #(st/emit! (dw/select-for-drawing :text))
|
|
||||||
:esc #(st/emit! :interrupt dw/deselect-all)
|
|
||||||
:delete #(st/emit! dw/delete-selected)
|
|
||||||
:ctrl+up #(st/emit! (dw/vertical-order-selected :up))
|
|
||||||
:ctrl+down #(st/emit! (dw/vertical-order-selected :down))
|
|
||||||
:ctrl+shift+up #(st/emit! (dw/vertical-order-selected :top))
|
|
||||||
:ctrl+shift+down #(st/emit! (dw/vertical-order-selected :bottom))
|
|
||||||
:shift+up #(st/emit! (dw/move-selected :up true))
|
|
||||||
:shift+down #(st/emit! (dw/move-selected :down true))
|
|
||||||
:shift+right #(st/emit! (dw/move-selected :right true))
|
|
||||||
:shift+left #(st/emit! (dw/move-selected :left true))
|
|
||||||
:up #(st/emit! (dw/move-selected :up false))
|
|
||||||
:down #(st/emit! (dw/move-selected :down false))
|
|
||||||
:right #(st/emit! (dw/move-selected :right false))
|
|
||||||
:left #(st/emit! (dw/move-selected :left false))
|
|
||||||
})
|
|
||||||
|
|
||||||
;; --- Shortcuts Setup Functions
|
|
||||||
|
|
||||||
(defn- watch-shortcuts
|
|
||||||
[sink]
|
|
||||||
(let [handler (KeyboardShortcutHandler. js/document)]
|
|
||||||
|
|
||||||
;; Register shortcuts.
|
|
||||||
(doseq [item (keys +shortcuts+)]
|
|
||||||
(let [identifier (name item)]
|
|
||||||
(.registerShortcut handler identifier identifier)))
|
|
||||||
|
|
||||||
;; Initialize shortcut listener.
|
|
||||||
(let [event KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED
|
|
||||||
callback #(sink (keyword (.-identifier %)))
|
|
||||||
key (events/listen handler event callback)]
|
|
||||||
(fn []
|
|
||||||
(events/unlistenByKey key)
|
|
||||||
(.clearKeyListener handler)))))
|
|
||||||
|
|
||||||
(defn init
|
|
||||||
[]
|
|
||||||
(let [stream (->> (rx/create watch-shortcuts)
|
|
||||||
(rx/pr-log "[debug]: shortcut:"))]
|
|
||||||
(rx/on-value stream (fn [event]
|
|
||||||
(when-let [handler (get +shortcuts+ event)]
|
|
||||||
(handler))))))
|
|
||||||
@ -7,8 +7,10 @@
|
|||||||
(ns uxbox.util.webapi
|
(ns uxbox.util.webapi
|
||||||
"HTML5 web api helpers."
|
"HTML5 web api helpers."
|
||||||
(:require
|
(:require
|
||||||
|
[promesa.core :as p]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]
|
||||||
|
[uxbox.util.transit :as t]))
|
||||||
|
|
||||||
(defn read-file-as-text
|
(defn read-file-as-text
|
||||||
[file]
|
[file]
|
||||||
@ -65,4 +67,19 @@
|
|||||||
;; (rx/create on-subscribe)))
|
;; (rx/create on-subscribe)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn write-to-clipboard
|
||||||
|
[data]
|
||||||
|
(let [cboard (unchecked-get js/navigator "clipboard")]
|
||||||
|
(.writeText cboard (uxbox.util.transit/encode data))))
|
||||||
|
|
||||||
|
(defn- read-from-clipboard
|
||||||
|
[]
|
||||||
|
(let [cboard (unchecked-get js/navigator "clipboard")]
|
||||||
|
(-> (.readText cboard)
|
||||||
|
(p/then (fn [data]
|
||||||
|
(try
|
||||||
|
(t/decode data)
|
||||||
|
(catch :default e
|
||||||
|
nil)))))))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user