mirror of
https://github.com/penpot/penpot.git
synced 2025-01-09 08:20:45 -05:00
Merge pull request #148 from uxbox/us/182/workspace-copy-paste
Copy&Paste preview
This commit is contained in:
commit
0c4c50270c
7 changed files with 248 additions and 155 deletions
|
@ -215,11 +215,14 @@
|
|||
|
||||
(defmethod process-change :del-obj
|
||||
[data {:keys [id] :as change}]
|
||||
(when-let [{:keys [frame-id] :as obj} (get-in data [:objects id])]
|
||||
(-> data
|
||||
(update :objects dissoc id)
|
||||
(update-in [:objects frame-id :shapes]
|
||||
(fn [s] (filterv #(not= % id) s))))))
|
||||
(when-let [{:keys [frame-id shapes] :as obj} (get-in data [:objects id])]
|
||||
(let [data (update data :objects dissoc id)]
|
||||
(cond-> data
|
||||
(contains? (:objects data) frame-id)
|
||||
(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
|
||||
[shape op]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; 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
|
||||
(:require
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
(:require
|
||||
[clojure.set :as set]
|
||||
[beicon.core :as rx]
|
||||
[goog.object :as gobj]
|
||||
[goog.events :as events]
|
||||
[cljs.spec.alpha :as s]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.common.data :as d]
|
||||
|
@ -36,7 +38,11 @@
|
|||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.transit :as t]
|
||||
[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
|
||||
(def clear-ruler nil)
|
||||
|
@ -358,6 +364,63 @@
|
|||
(let [local (:workspace-local state)]
|
||||
(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
|
||||
[ma mb]
|
||||
(let [ma-keys (set (keys ma))
|
||||
|
@ -410,60 +473,6 @@
|
|||
(when-not (empty? 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
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -896,7 +905,7 @@
|
|||
in the current workspace page."
|
||||
[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))))
|
||||
|
||||
(defn impl-generate-unique-name
|
||||
|
@ -1043,14 +1052,12 @@
|
|||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:frame-id frame-id
|
||||
:obj (assoc obj :frame-id frame-id)
|
||||
:session-id (:session-id state)}))
|
||||
:obj (assoc obj :frame-id frame-id)}))
|
||||
(:shapes frame))
|
||||
|
||||
uchanges (mapv (fn [rch]
|
||||
{:type :del-obj
|
||||
:id (:id rch)
|
||||
:session-id (:session-id state)})
|
||||
:id (:id rch)})
|
||||
rchanges)
|
||||
|
||||
shapes (mapv :id rchanges)
|
||||
|
@ -1068,7 +1075,7 @@
|
|||
:id frame-id
|
||||
:session-id (:session-id state)}]
|
||||
(rx/of (commit-changes (d/concat [rchange] rchanges)
|
||||
(d/concat [uchange] uchanges)
|
||||
(d/concat [] uchanges [uchange])
|
||||
{:commit-local? true}))))))
|
||||
|
||||
|
||||
|
@ -1096,7 +1103,6 @@
|
|||
(rx/empty))))))
|
||||
|
||||
|
||||
|
||||
;; --- Toggle shape's selection status (selected or deselected)
|
||||
|
||||
(defn select-shape
|
||||
|
@ -1979,6 +1985,95 @@
|
|||
(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
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -1998,3 +2093,63 @@
|
|||
pages (vec (concat before [id] after))]
|
||||
(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)))
|
||||
]
|
||||
|
||||
[:svg {:x x :y y :width width :height height}
|
||||
[:& "rect" props]
|
||||
(for [item (reverse childs)]
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
[uxbox.main.ui.workspace.header :refer [header]]
|
||||
[uxbox.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
|
||||
[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.history :refer [history-dialog]]
|
||||
[uxbox.main.ui.workspace.left-toolbar :refer [left-toolbar]]
|
||||
|
@ -119,10 +118,11 @@
|
|||
{:fn #(st/emit! dw/initialize-layout)})
|
||||
|
||||
(mf/use-effect
|
||||
{:deps (mf/deps file-id page-id)
|
||||
{:deps (mf/deps file-id)
|
||||
:fn (fn []
|
||||
(let [sub (shortcuts/init)]
|
||||
#(rx/cancel! sub)))})
|
||||
(st/emit! dw/initialize-shortcuts)
|
||||
#(st/emit! ::dw/finalize-shortcuts))})
|
||||
|
||||
(let [file (mf/deref refs/workspace-file)
|
||||
page (mf/deref refs/workspace-page)
|
||||
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
|
||||
"HTML5 web api helpers."
|
||||
(:require
|
||||
[promesa.core :as p]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]))
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.util.transit :as t]))
|
||||
|
||||
(defn read-file-as-text
|
||||
[file]
|
||||
|
@ -65,4 +67,19 @@
|
|||
;; (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…
Reference in a new issue