Files
penpot/src/uxbox/main/data/workspace.cljs
2016-10-23 17:23:17 +02:00

255 lines
7.4 KiB
Clojure

;; 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.data.workspace
(:require [beicon.core :as rx]
[uxbox.util.uuid :as uuid]
[uxbox.main.constants :as c]
[uxbox.util.rstore :as rs]
[uxbox.util.schema :as sc]
[uxbox.util.geom.point :as gpt]
[uxbox.util.workers :as uw]
[uxbox.main.state :as st]
[uxbox.main.state.shapes :as stsh]
[uxbox.main.data.core :refer (worker)]
[uxbox.main.data.projects :as dp]
[uxbox.main.data.pages :as udp]
[uxbox.main.data.shapes :as uds]
[uxbox.main.data.forms :as udf]
[uxbox.main.data.lightbox :as udl]
[uxbox.main.data.history :as udh]
[uxbox.util.datetime :as dt]
[uxbox.util.math :as mth]
[uxbox.util.data :refer (index-of)]))
;; --- Constants
(def zoom-levels
[0.20 0.21 0.22 0.23 0.24 0.25 0.27 0.28 0.30 0.32 0.34
0.36 0.38 0.40 0.42 0.44 0.46 0.48 0.51 0.54 0.57 0.60
0.63 0.66 0.69 0.73 0.77 0.81 0.85 0.90 0.95 1.00 1.05
1.10 1.15 1.21 1.27 1.33 1.40 1.47 1.54 1.62 1.70 1.78
1.87 1.96 2.06 2.16 2.27 2.38 2.50 2.62 2.75 2.88 3.00])
;; --- Initialize Workspace
(declare initialize-alignment-index)
(defrecord InitializeWorkspace [project page]
rs/UpdateEvent
(-apply-update [_ state]
(if (:workspace state)
(update state :workspace merge
{:project project
:page page
:selected #{}
:drawing nil})
(assoc state :workspace
{:project project
:zoom 1
:page page
:flags #{:sitemap :drawtools :layers}
:selected #{}
:drawing nil})))
rs/WatchEvent
(-apply-watch [_ state s]
(let [page-id page
page (get-in state [:pages-by-id page-id])]
;; Activate loaded if page is not fetched.
(when-not page (reset! st/loader true))
(rx/merge
(if page
(rx/of (initialize-alignment-index page-id))
(rx/merge
(rx/of (udp/fetch-pages project))
(->> (rx/filter udp/pages-fetched? s)
(rx/take 1)
(rx/do #(reset! st/loader false))
(rx/map #(initialize-alignment-index page-id)))))
;; Initial history loading
(rx/of
(udh/fetch-page-history page-id)
(udh/fetch-pinned-page-history page-id)))))
rs/EffectEvent
(-apply-effect [_ state]
;; Optimistic prefetch of projects if them are not already fetched
(when-not (seq (:projects-by-id state))
(rs/emit! (dp/fetch-projects)))))
(defn initialize
"Initialize the workspace state."
[project page]
(InitializeWorkspace. project page))
;; --- Toggle Flag
(defn toggle-flag
"Toggle the enabled flag of the specified tool."
[key]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(let [flags (get-in state [:workspace :flags])]
(if (contains? flags key)
(assoc-in state [:workspace :flags] (disj flags key))
(assoc-in state [:workspace :flags] (conj flags key)))))))
(defn select-for-drawing
"Mark a shape selected for drawing in the canvas."
[shape]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(let [current (get-in state [:workspace :drawing])]
(if (or (nil? shape)
(= shape current))
(update state :workspace dissoc :drawing)
(assoc-in state [:workspace :drawing] shape))))))
;; --- Activate Workspace Flag
(defrecord ActivateFlag [flag]
rs/UpdateEvent
(-apply-update [_ state]
(update-in state [:workspace :flags] conj flag)))
(defn activate-flag
[flag]
(ActivateFlag. flag))
;; --- Copy to Clipboard
(defrecord CopyToClipboard []
rs/UpdateEvent
(-apply-update [_ state]
(let [selected (get-in state [:workspace :selected])
item {:id (uuid/random)
: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)))
;; --- Increase Zoom
(defrecord IncreaseZoom []
rs/UpdateEvent
(-apply-update [_ state]
(let [increase #(nth zoom-levels
(+ (index-of zoom-levels %) 1)
(last zoom-levels))]
(update-in state [:workspace :zoom] (fnil increase 1)))))
(defn increase-zoom
[]
(IncreaseZoom.))
;; --- Decrease Zoom
(defrecord DecreaseZoom []
rs/UpdateEvent
(-apply-update [_ state]
(let [decrease #(nth zoom-levels
(- (index-of zoom-levels %) 1)
(first zoom-levels))]
(update-in state [:workspace :zoom] (fnil decrease 1)))))
(defn decrease-zoom
[]
(DecreaseZoom.))
;; --- Reset Zoom
(defrecord ResetZoom []
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:workspace :zoom] 1)))
(defn reset-zoom
[]
(ResetZoom.))
;; --- Initialize Alignment Index
(defrecord InitializeAlignmentIndex [id]
rs/WatchEvent
(-apply-watch [_ state s]
(let [page (get-in state [:pages-by-id id])
opts (:options page)
message {:cmd :grid/init
:width c/viewport-width
:height c/viewport-height
:x-axis (:grid/x-axis opts c/grid-x-axis)
:y-axis (:grid/y-axis opts c/grid-y-axis)}]
(rx/merge
(->> (uw/send! worker message)
(rx/map #(activate-flag :grid/indexed)))
(when (:grid/alignment opts)
(rx/of (activate-flag :grid/alignment)))))))
(defn initialize-alignment-index
[id]
(InitializeAlignmentIndex. id))
;; --- Update Workspace Settings (Form)
(defrecord SubmitWorkspaceSettings [id options]
rs/WatchEvent
(-apply-watch [_ state s]
(rx/of (udp/update-page-options id options)
(initialize-alignment-index id)
(udf/clean :workspace/settings)))
rs/EffectEvent
(-apply-effect [_ state]
(udl/close!)))
(def submit-workspace-settings-schema
{:grid/y-axis [sc/required sc/integer [sc/in-range 2 100]]
:grid/x-axis [sc/required sc/integer [sc/in-range 2 100]]
:grid/alignment [sc/boolean]
:grid/color [sc/required sc/color]})
(defn submit-workspace-settings
[id data]
(let [schema submit-workspace-settings-schema
[errors data] (sc/validate data schema)]
(if errors
(udf/assign-errors :workspace/settings errors)
(SubmitWorkspaceSettings. id data))))