feat(frontend): refactor many workspace components (rumext update)

This commit is contained in:
Andrey Antukh 2019-07-18 12:29:29 +02:00
parent c4d7d545ae
commit 9ddd9f317d
15 changed files with 504 additions and 526 deletions

View file

@ -2,78 +2,42 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.workspace (ns uxbox.main.ui.workspace
(:require [beicon.core :as rx] (:require
[lentes.core :as l] [beicon.core :as rx]
[uxbox.main.store :as st] [lentes.core :as l]
[uxbox.main.constants :as c] [rumext.core :as mx :include-macros true]
[uxbox.main.refs :as refs] [uxbox.main.constants :as c]
[uxbox.main.streams :as streams] [uxbox.main.data.history :as udh]
[uxbox.main.data.workspace :as dw] [uxbox.main.data.pages :as udp]
[uxbox.main.data.pages :as udp] [uxbox.main.data.undo :as udu]
[uxbox.main.data.history :as udh] [uxbox.main.data.workspace :as dw]
[uxbox.main.data.undo :as udu] [uxbox.main.refs :as refs]
[uxbox.main.user-events :as uev] [uxbox.main.store :as st]
[uxbox.main.ui.messages :refer [messages-widget]] [uxbox.main.streams :as streams]
[uxbox.main.ui.confirm] [uxbox.main.ui.confirm]
[uxbox.main.ui.workspace.images] [uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.messages :refer [messages-widget]]
[uxbox.main.ui.workspace.scroll :as scroll] [uxbox.main.ui.workspace.canvas :refer [viewport]]
[uxbox.main.ui.workspace.download] [uxbox.main.ui.workspace.colorpalette :refer [colorpalette]]
[uxbox.main.ui.workspace.shortcuts :refer [shortcuts-mixin]] [uxbox.main.ui.workspace.download]
[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.images]
[uxbox.main.ui.workspace.sidebar.history :refer [history-dialog]] [uxbox.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
[uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]] [uxbox.main.ui.workspace.scroll :as scroll]
[uxbox.main.ui.workspace.colorpalette :refer [colorpalette]] [uxbox.main.ui.workspace.shortcuts :refer [shortcuts-mixin]]
[uxbox.main.ui.workspace.canvas :refer [viewport]] [uxbox.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
[uxbox.util.dom :as dom] [uxbox.main.ui.workspace.sidebar.history :refer [history-dialog]]
[uxbox.util.geom.point :as gpt] [uxbox.main.user-events :as uev]
[uxbox.util.data :refer [classnames]] [uxbox.util.data :refer [classnames]]
[rumext.core :as mx :include-macros true])) [uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]))
;; --- Workspace ;; --- Workspace
(defn- workspace-will-mount
[own]
(let [[projectid pageid] (:rum/args own)]
(st/emit! (dw/initialize projectid pageid))
own))
(defn- workspace-did-mount
[own]
(let [[projectid pageid] (:rum/args own)
dom (mx/ref-node own "workspace-canvas")
scroll-to-page-center #(scroll/scroll-to-page-center dom @refs/selected-page)
sub (rx/subscribe streams/page-id-ref-s scroll-to-page-center)]
(scroll-to-page-center)
(st/emit! (udp/watch-page-changes pageid)
(udu/watch-page-changes pageid))
(assoc own ::sub sub)))
(defn- workspace-will-unmount
[own]
(st/emit! ::udp/stop-page-watcher)
(rx/cancel! (::sub own))
(dissoc own ::sub))
(defn- workspace-did-remount
[old-state state]
(let [[projectid pageid] (:rum/args state)
[oldprojectid oldpageid] (:rum/args old-state)]
(when (not= pageid oldpageid)
(st/emit! (dw/initialize projectid pageid)
::udp/stop-page-watcher
(udp/watch-page-changes pageid)
(udu/watch-page-changes pageid)))
state))
(defn- on-scroll (defn- on-scroll
[event] [event]
(let [target (.-target event) (let [target (.-target event)
@ -99,52 +63,76 @@
(-> (l/key :page) (-> (l/key :page)
(l/derive refs/workspace))) (l/derive refs/workspace)))
(mx/defcs workspace (mx/def workspace
{:did-remount workspace-did-remount :key-fn vector
:will-mount workspace-will-mount :mixins #{mx/static
:will-unmount workspace-will-unmount
:did-mount workspace-did-mount
:mixins [mx/static
mx/reactive mx/reactive
shortcuts-mixin]} shortcuts-mixin}
[own project-id page-id]
(let [flags (mx/react refs/flags)
left-sidebar? (not (empty? (keep flags [:layers :sitemap
:document-history])))
right-sidebar? (not (empty? (keep flags [:icons :drawtools
:element-options])))
classes (classnames
:no-tool-bar-right (not right-sidebar?)
:no-tool-bar-left (not left-sidebar?)
:scrolling (:viewport-positionig workspace))]
[:div {}
(messages-widget)
(header)
(colorpalette)
[:main.main-content {} :init
[:section.workspace-content (fn [own {:keys [project page] :as props}]
{:class classes (st/emit! (dw/initialize project page))
:on-scroll on-scroll (assoc own ::canvas (mx/create-ref)))
:on-wheel (partial on-wheel own)}
(history-dialog) :did-mount
(fn [own]
(let [{:keys [project page]} (::mx/props own)
;; dom (mx/ref-node own "workspace-canvas")
dom (mx/ref-node (::canvas own))
scroll-to-page-center #(scroll/scroll-to-page-center dom @refs/selected-page)
sub (rx/subscribe streams/page-id-ref-s scroll-to-page-center)]
(scroll-to-page-center)
(st/emit! (udp/watch-page-changes page)
(udu/watch-page-changes page))
(assoc own ::sub sub)))
;; Rules :will-unmount
(when (contains? flags :rules) (fn [own]
(horizontal-rule)) (st/emit! ::udp/stop-page-watcher)
(rx/cancel! (::sub own))
(dissoc own ::sub))
(when (contains? flags :rules) :render
(vertical-rule)) (fn [own props]
;; [own project-id page-id]
(let [flags (mx/react refs/flags)
project-id (get-in own [::mx/props :project])
page-id (get-in own [::mx/props :page])
left-sidebar? (not (empty? (keep flags [:layers :sitemap
:document-history])))
right-sidebar? (not (empty? (keep flags [:icons :drawtools
:element-options])))
classes (classnames
:no-tool-bar-right (not right-sidebar?)
:no-tool-bar-left (not left-sidebar?)
:scrolling (:viewport-positionig workspace))]
[:*
(messages-widget)
(header)
(colorpalette)
;; Canvas [:main.main-content
[:section.workspace-canvas [:section.workspace-content
{:id "workspace-canvas" {:class classes
:ref "workspace-canvas"} :on-scroll on-scroll
(viewport)]] :on-wheel (partial on-wheel own)}
;; Aside (history-dialog)
(when left-sidebar?
(left-sidebar flags page-id)) ;; Rules
(when right-sidebar? (when (contains? flags :rules)
(right-sidebar flags page-id))]])) (horizontal-rule))
(when (contains? flags :rules)
(vertical-rule))
;; Canvas
[:section.workspace-canvas {:id "workspace-canvas"
:ref (::canvas own)}
(viewport)]]
;; Aside
(when left-sidebar?
(left-sidebar {:flags flags :page-id page-id}))
(when right-sidebar?
(right-sidebar {:flags flags :page-id page-id}))]])))

View file

@ -244,7 +244,7 @@
opts {:shift? shift? opts {:shift? shift?
:ctrl? ctrl?}] :ctrl? ctrl?}]
(st/emit! (uev/mouse-event :double-click ctrl? shift?))))] (st/emit! (uev/mouse-event :double-click ctrl? shift?))))]
[:div {} [:*
(coordinates) (coordinates)
[:div.tooltip-container {} [:div.tooltip-container {}
(when tooltip (when tooltip

View file

@ -30,13 +30,13 @@
(on-close [event] (on-close [event]
(dom/prevent-default event) (dom/prevent-default event)
(udl/close!))] (udl/close!))]
[:div.lightbox-body.clipboard {} [:div.lightbox-body.clipboard
[:div.clipboard-list {} [:div.clipboard-list
(for [item (mx/react clipboard-ref)] (for [item (mx/react clipboard-ref)]
[:div.clipboard-item [:div.clipboard-item
{:key (str (:id item)) {:key (str (:id item))
:on-click (partial on-paste item)} :on-click (partial on-paste item)}
[:span.clipboard-icon {} i/box] [:span.clipboard-icon i/box]
[:span {} (str "Copied (" (dt/timeago (:created-at item)) ")")]])] [:span {} (str "Copied (" (dt/timeago (:created-at item)) ")")]])]
[:a.close {:href "#" :on-click on-close} i/close]])) [:a.close {:href "#" :on-click on-close} i/close]]))

View file

@ -46,7 +46,7 @@
[:span.color-text {} rgb-color]]))) [:span.color-text {} rgb-color]])))
(defn- palette-after-render (defn- palette-after-render
[{:keys [rum/local] :as own}] [{:keys [::mx/local] :as own}]
(let [dom (mx/ref-node own "container") (let [dom (mx/ref-node own "container")
width (.-clientWidth dom)] width (.-clientWidth dom)]
(when (not= (:width @local) width) (when (not= (:width @local) width)
@ -60,7 +60,7 @@
(mx/defcs palette (mx/defcs palette
{:mixins [mx/static mx/reactive (mx/local)] {:mixins [mx/static mx/reactive (mx/local)]
:after-render palette-after-render} :after-render palette-after-render}
[{:keys [rum/local] :as own}] [{:keys [::mx/local] :as own}]
(let [collections (->> (mx/react collections-ref) (let [collections (->> (mx/react collections-ref)
(vals) (vals)
(filter :id) (filter :id)
@ -112,14 +112,14 @@
[:span.close-palette {:on-click close} [:span.close-palette {:on-click close}
i/close]]))) i/close]])))
(defn- colorpalette-will-mount (defn- colorpalette-init
[own] [own]
(st/emit! (dc/fetch-collections)) (st/emit! (dc/fetch-collections))
own) own)
(mx/defc colorpalette (mx/defc colorpalette
{:mixins [mx/static mx/reactive] {:mixins [mx/static mx/reactive]
:will-mount colorpalette-will-mount} :init colorpalette-init}
[] []
(let [flags (mx/react refs/flags)] (let [flags (mx/react refs/flags)]
(when (contains? flags :colorpalette) (when (contains? flags :colorpalette)

View file

@ -2,24 +2,25 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.workspace.images (ns uxbox.main.ui.workspace.images
(:require [lentes.core :as l] (:require
[rumext.core :as mx :include-macros true] [lentes.core :as l]
[potok.core :as ptk] [potok.core :as ptk]
[uxbox.builtins.icons :as i] [rumext.core :as mx :include-macros true]
[uxbox.main.store :as st] [uxbox.builtins.icons :as i]
[uxbox.main.data.lightbox :as udl] [uxbox.main.data.images :as udi]
[uxbox.main.data.images :as udi] [uxbox.main.data.lightbox :as udl]
[uxbox.main.data.workspace :as udw] [uxbox.main.data.shapes :as uds]
[uxbox.main.data.shapes :as uds] [uxbox.main.data.workspace :as udw]
[uxbox.main.ui.lightbox :as lbx] [uxbox.main.store :as st]
[uxbox.util.i18n :as t :refer [tr]] [uxbox.main.ui.lightbox :as lbx]
[uxbox.util.data :refer [read-string jscoll->vec]] [uxbox.util.data :refer [read-string jscoll->vec]]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.uuid :as uuid])) [uxbox.util.i18n :as t :refer [tr]]
[uxbox.util.uuid :as uuid]))
;; --- Refs ;; --- Refs
@ -113,9 +114,9 @@
(-> (image-item image) (-> (image-item image)
(mx/with-key (str (:id image)))))]) (mx/with-key (str (:id image)))))])
(defn will-mount (defn init
[own] [own]
(let [local (:rum/local own)] (let [local (::mx/local own)]
(st/emit! (udi/fetch-collections)) (st/emit! (udi/fetch-collections))
(st/emit! (udi/fetch-images nil)) (st/emit! (udi/fetch-images nil))
(add-watch local ::key (fn [_ _ _ v] (add-watch local ::key (fn [_ _ _ v]
@ -124,16 +125,16 @@
(defn will-unmount (defn will-unmount
[own] [own]
(let [local (:rum/local own)] (let [local (::mx/local own)]
(remove-watch local ::key) (remove-watch local ::key)
own)) own))
(mx/defcs image-collections-lightbox (mx/defcs image-collections-lightbox
{:mixins [mx/reactive (mx/local)] {:mixins [mx/reactive (mx/local)]
:will-mount will-mount :init init
:will-unmount will-unmount} :will-unmount will-unmount}
[own] [own]
(let [local (:rum/local own) (let [local (::mx/local own)
id (:id @local) id (:id @local)
type (:type @local :own) type (:type @local :own)
own? (= type :own) own? (= type :own)

View file

@ -6,13 +6,14 @@
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.rules (ns uxbox.main.ui.workspace.rules
(:require [cuerdas.core :as str] (:require
[beicon.core :as rx] [beicon.core :as rx]
[uxbox.main.store :as s] [cuerdas.core :as str]
[uxbox.main.constants :as c] [rumext.core :as mx]
[uxbox.main.refs :as refs] [uxbox.main.constants :as c]
[uxbox.util.dom :as dom] [uxbox.main.refs :as refs]
[rumext.core :as mx :include-macros true])) [uxbox.main.store :as s]
[uxbox.util.dom :as dom]))
;; --- Constants & Helpers ;; --- Constants & Helpers
@ -65,102 +66,103 @@
;; --- Horizontal Text Label ;; --- Horizontal Text Label
(mx/defc horizontal-text-label (mx/def horizontal-text-label
[zoom value] :key-fn second
(let [big-ticks-mod (big-ticks-mod zoom) :render
pos (+ (* value zoom) (fn [own [zoom value]]
rule-padding (let [big-ticks-mod (big-ticks-mod zoom)
(* c/canvas-start-x zoom) pos (+ (* value zoom)
c/canvas-scroll-padding)] rule-padding
(when (< (mod value big-ticks-mod) step-size) (* c/canvas-start-x zoom)
[:text {:x (+ pos 2) c/canvas-scroll-padding)]
:y 13 (when (< (mod value big-ticks-mod) step-size)
:key (str pos) [:text {:x (+ pos 2)
:fill "#9da2a6" :y 13
:style {:font-size "12px"}} :key (str pos)
value]))) :fill "#9da2a6"
:style {:font-size "12px"}}
value]))))
;; --- Horizontal Text Label ;; --- Horizontal Text Label
(mx/defc vertical-text-label (mx/def vertical-text-label
[zoom value] :key-fn second
(let [big-ticks-mod (big-ticks-mod zoom) :render
pos (+ (* value zoom) (fn [own [zoom value]]
(* c/canvas-start-x zoom) (let [big-ticks-mod (big-ticks-mod zoom)
;; c/canvas-start-x pos (+ (* value zoom)
c/canvas-scroll-padding)] (* c/canvas-start-x zoom)
(when (< (mod value big-ticks-mod) step-size) c/canvas-scroll-padding)]
[:text {:y (- pos 3) (when (< (mod value big-ticks-mod) step-size)
:x 5 [:text {:y (- pos 3)
:key (str pos) :x 5
:fill "#9da2a6" :key (str pos)
:transform (str/format "rotate(90 0 %s)" pos) :fill "#9da2a6"
:style {:font-size "12px"}} :transform (str/format "rotate(90 0 %s)" pos)
value]))) :style {:font-size "12px"}}
value]))))
;; --- Horizontal Rule Ticks (Component) ;; --- Horizontal Rule Ticks (Component)
(mx/defc horizontal-rule-ticks (mx/def horizontal-rule-ticks
{:mixins [mx/static]} :mixins #{mx/static}
[zoom] :render
(let [zoom (or zoom 1) (fn [own zoom]
path (reduce (partial make-vertical-tick zoom) [] +ticks+) (let [zoom (or zoom 1)
labels (->> (map (partial horizontal-text-label zoom) +ticks+) path (reduce (partial make-vertical-tick zoom) [] +ticks+)]
(filterv identity))] [:g
[:g {} [:path {:d (str/join " " path)}]
[:path {:d (str/join " " path)}] (for [tick +ticks+]
(for [tick +ticks+] (horizontal-text-label [zoom tick]))])))
(-> (horizontal-text-label zoom tick)
(mx/with-key (str tick))))]))
;; --- Vertical Rule Ticks (Component) ;; --- Vertical Rule Ticks (Component)
(mx/defc vertical-rule-ticks (mx/def vertical-rule-ticks
{:mixins [mx/static]} :mixins #{mx/static}
[zoom] :render
(let [zoom (or zoom 1) (fn [own zoom]
path (reduce (partial make-horizontal-tick zoom) [] +ticks+) (let [zoom (or zoom 1)
labels (->> (map (partial vertical-text-label zoom) +ticks+) path (reduce (partial make-horizontal-tick zoom) [] +ticks+)]
(filterv identity))] [:g
[:g {} [:path {:d (str/join " " path)}]
[:path {:d (str/join " " path)}] (for [tick +ticks+]
(for [tick +ticks+] (vertical-text-label [zoom tick]))])))
(-> (vertical-text-label zoom tick)
(mx/with-key (str tick))))]))
;; --- Horizontal Rule (Component) ;; --- Horizontal Rule (Component)
(mx/defc horizontal-rule (mx/def horizontal-rule
{:mixins [mx/static mx/reactive]} :mixins #{mx/static mx/reactive}
[] :render
(let [scroll (mx/react refs/workspace-scroll) (fn [own props]
zoom (mx/react refs/selected-zoom) (let [scroll (mx/react refs/workspace-scroll)
scroll-x (:x scroll) zoom (mx/react refs/selected-zoom)
translate-x (- (- c/canvas-scroll-padding) (:x scroll))] scroll-x (:x scroll)
[:svg.horizontal-rule translate-x (- (- c/canvas-scroll-padding) (:x scroll))]
{:width c/viewport-width [:svg.horizontal-rule
:height 20} {:width c/viewport-width
[:rect {:height 20 :height 20}
:width c/viewport-width}] [:rect {:height 20
[:g {:transform (str "translate(" translate-x ", 0)")} :width c/viewport-width}]
(horizontal-rule-ticks zoom)]])) [:g {:transform (str "translate(" translate-x ", 0)")}
(horizontal-rule-ticks zoom)]])))
;; --- Vertical Rule (Component) ;; --- Vertical Rule (Component)
(mx/defc vertical-rule (mx/def vertical-rule
{:mixins [mx/static mx/reactive]} :mixins #{mx/static mx/reactive}
[] :render
(let [scroll (mx/react refs/workspace-scroll) (fn [own props]
zoom (mx/react refs/selected-zoom) (let [scroll (mx/react refs/workspace-scroll)
scroll-y (:y scroll) zoom (mx/react refs/selected-zoom)
translate-y (- (- c/canvas-scroll-padding) (:y scroll))] scroll-y (:y scroll)
[:svg.vertical-rule translate-y (- (- c/canvas-scroll-padding) (:y scroll))]
{:width 20 [:svg.vertical-rule
:height c/viewport-height} {:width 20
:height c/viewport-height}
[:g {:transform (str "translate(0, " translate-y ")")} [:g {:transform (str "translate(0, " translate-y ")")}
(vertical-rule-ticks zoom)] (vertical-rule-ticks zoom)]
[:rect {:x 0 [:rect {:x 0
:y 0 :y 0
:height 20 :height 20
:width 20}]])) :width 20}]])))

View file

@ -98,7 +98,7 @@
;; --- Mixin ;; --- Mixin
(defn- will-mount (defn- init
[own] [own]
(assoc own ::sub (initialize))) (assoc own ::sub (initialize)))
@ -108,5 +108,5 @@
(dissoc own ::sub)) (dissoc own ::sub))
(def shortcuts-mixin (def shortcuts-mixin
{:will-mount will-mount {:init init
:will-unmount will-unmount}) :will-unmount will-unmount})

View file

@ -18,10 +18,9 @@
;; --- Left Sidebar (Component) ;; --- Left Sidebar (Component)
(mx/defc left-sidebar (mx/defc left-sidebar
{:mixins [mx/static]} [{:keys [flags page-id] :as props}]
[flags page-id] [:aside#settings-bar.settings-bar.settings-bar-left
[:aside#settings-bar.settings-bar.settings-bar-left {} [:div.settings-bar-inside
[:div.settings-bar-inside {}
(when (contains? flags :sitemap) (when (contains? flags :sitemap)
(sitemap-toolbox page-id)) (sitemap-toolbox page-id))
(when (contains? flags :document-history) (when (contains? flags :document-history)
@ -32,10 +31,9 @@
;; --- Right Sidebar (Component) ;; --- Right Sidebar (Component)
(mx/defc right-sidebar (mx/defc right-sidebar
{:mixins [mx/static]} [{:keys [flags page-id] :as props}]
[flags page-id] [:aside#settings-bar.settings-bar
[:aside#settings-bar.settings-bar {} [:div.settings-bar-inside
[:div.settings-bar-inside {}
(when (contains? flags :drawtools) (when (contains? flags :drawtools)
(draw-toolbox flags)) (draw-toolbox flags))
(when (contains? flags :element-options) (when (contains? flags :element-options)

View file

@ -21,125 +21,115 @@
;; --- History Item (Component) ;; --- History Item (Component)
(mx/defc history-item (mx/def history-item
{:mixins [mx/static]} :mixins [mx/static]
[item selected] :key-fn :id
(letfn [(on-select [event] :render
(dom/prevent-default event) (fn [own {:keys [::selected] :as item}]
(st/emit! (udh/select-page-history (:version item)))) (letfn [(on-select [event]
(on-pinned [event] (dom/prevent-default event)
(dom/prevent-default event) (st/emit! (udh/select-page-history (:version item))))
(dom/stop-propagation event) (on-pinned [event]
(let [item (assoc item (dom/prevent-default event)
:label "no label" (dom/stop-propagation event)
:pinned (not (:pinned item)))] (let [item (assoc item
(st/emit! (udh/update-history-item item))))] :label "no label"
[:li {:class (when (= selected (:version item)) "current") :pinned (not (:pinned item)))]
:on-click on-select} (st/emit! (udh/update-history-item item))))]
[:div.pin-icon {:on-click on-pinned [:li {:class (when (= selected (:version item)) "current")
:class (when (:pinned item) "selected")} :on-click on-select}
i/pin] [:div.pin-icon {:on-click on-pinned
[:span {} (str "Version " (:version item) :class (when (:pinned item) "selected")}
" (" (dt/timeago (:created-at item)) ")")]])) i/pin]
[:span (str "Version " (:version item)
" (" (dt/timeago (:created-at item)) ")")]])))
;; --- History List (Component) ;; --- History List (Component)
(mx/defc history-list (mx/def history-list
{:mixins [mx/static mx/reactive]} :mixins [mx/static mx/reactive]
[{:keys [selected items min-version] :as history}] :render
(let [items (reverse (sort-by :version items)) (fn [own {:keys [selected items min-version] :as history}]
page (mx/react refs/selected-page) (let [items (reverse (sort-by :version items))
show-more? (pos? min-version) page (mx/react refs/selected-page)
load-more #(st/emit! (udh/load-more))] show-more? (pos? min-version)
[:ul.history-content {} load-more #(st/emit! (udh/load-more))]
(for [item items] [:ul.history-content
(let [current? (= (:version item) (:version page))] (for [item items]
(-> (history-item item selected current?) (history-item (assoc item ::selectd selected)))
(mx/with-key (str (:id item)))))) (when show-more?
(when show-more? [:li {:on-click load-more}
[:li {:on-click load-more} [:a.btn-primary.btn-small
[:a.btn-primary.btn-small {} "view more"]])])))
"view more"]])]))
;; --- History Pinned List (Component) ;; --- History Pinned List (Component)
(mx/defc history-pinned-list (mx/def history-pinned-list
{:mixins [mx/static]} :mixins [mx/static]
[{:keys [pinned selected] :as history}] :render
[:ul.history-content {} (fn [own {:keys [pinned selected] :as history}]
(for [item (reverse (sort-by :version pinned))] [:ul.history-content
(let [selected? (= (:version item) selected)] (for [item (reverse (sort-by :version pinned))]
(-> (history-item item selected?) (let [selected (= (:version item) selected)]
(mx/with-key (str (:id item))))))]) (history-item (assoc item ::selected selected))))]))
;; --- History Toolbox (Component) ;; --- History Toolbox (Component)
(mx/def history-toolbox
:mixins [mx/static mx/reactive]
(defn- history-toolbox-will-mount :init
[own] (fn [own page-id]
(let [[page-id] (:rum/args own)]
(st/emit! (udh/initialize page-id)) (st/emit! (udh/initialize page-id))
own)) own)
(defn- history-toolbox-did-remount :will-unmount
[oldown own] (fn [own]
(let [[old-page-id] (:rum/args oldown) (st/emit! ::udh/stop-changes-watcher)
[new-page-id] (:rum/args own)] own)
(when-not (= old-page-id new-page-id)
(st/emit! ::udh/stop-changes-watcher
(udh/initialize new-page-id)))
own))
(defn- history-toolbox-will-unmount :render
[own] (fn [own page-id]
(st/emit! ::udh/stop-changes-watcher) (let [history (mx/react refs/history)
own) section (:section history :main)
(mx/defc history-toolbox close #(st/emit! (dw/toggle-flag :document-history))
{:mixins [mx/static mx/reactive] main? (= section :main)
:will-mount history-toolbox-will-mount pinned? (= section :pinned)
:will-unmount history-toolbox-will-unmount
:did-remount history-toolbox-did-remount}
[_]
(let [history (mx/react refs/history)
section (:section history :main)
close #(st/emit! (dw/toggle-flag :document-history)) show-main #(st/emit! (udh/select-section :main))
main? (= section :main) show-pinned #(st/emit! (udh/select-section :pinned))]
pinned? (= section :pinned) [:div.document-history.tool-window {}
[:div.tool-window-bar {}
show-main #(st/emit! (udh/select-section :main)) [:div.tool-window-icon {} i/undo-history]
show-pinned #(st/emit! (udh/select-section :pinned))] [:span {} (tr "ds.document-history")]
[:div.document-history.tool-window {} [:div.tool-window-close {:on-click close} i/close]]
[:div.tool-window-bar {} [:div.tool-window-content {}
[:div.tool-window-icon {} i/undo-history] [:ul.history-tabs {}
[:span {} (tr "ds.document-history")] [:li {:on-click show-main
[:div.tool-window-close {:on-click close} i/close]] :class (when main? "selected")}
[:div.tool-window-content {} "History"]
[:ul.history-tabs {} [:li {:on-click show-pinned
[:li {:on-click show-main :class (when pinned? "selected")}
:class (when main? "selected")} "Pinned"]]
"History"] (if (= section :pinned)
[:li {:on-click show-pinned (history-pinned-list history)
:class (when pinned? "selected")} (history-list history))]])))
"Pinned"]]
(if (= section :pinned)
(history-pinned-list history)
(history-list history))]]))
;; --- History Dialog ;; --- History Dialog
(mx/defc history-dialog (mx/def history-dialog
{:mixins [mx/static mx/reactive]} :mixins [mx/static mx/reactive]
[] :render
(let [history (mx/react refs/history) (fn [own]
version (:selected history) (let [history (mx/react refs/history)
on-accept #(st/emit! (udh/apply-selected-history)) version (:selected history)
on-cancel #(st/emit! (udh/deselect-page-history))] on-accept #(st/emit! (udh/apply-selected-history))
(when (or version (:deselecting history)) on-cancel #(st/emit! (udh/deselect-page-history))]
[:div.message-version (when (or version (:deselecting history))
{:class (when (:deselecting history) "hide-message")} [:div.message-version
[:span {} (tr "history.alert-message" (or version "00")) {:class (when (:deselecting history) "hide-message")}
[:div.message-action {} [:span {} (tr "history.alert-message" (or version "00"))
[:a.btn-transparent {:on-click on-accept} "Accept"] [:div.message-action {}
[:a.btn-transparent {:on-click on-cancel} "Cancel"]]]]))) [:a.btn-transparent {:on-click on-accept} "Accept"]
[:a.btn-transparent {:on-click on-cancel} "Cancel"]]]]))))

View file

@ -40,14 +40,14 @@
[icon] [icon]
(icon/icon-svg icon)) (icon/icon-svg icon))
(defn- icons-toolbox-will-mount (defn- icons-toolbox-init
[own] [own]
(st/emit! (udw/initialize-icons-toolbox)) (st/emit! (udw/initialize-icons-toolbox))
own) own)
(mx/defc icons-toolbox (mx/defc icons-toolbox
{:mixins [mx/static mx/reactive] {:mixins [mx/static mx/reactive]
:will-mount icons-toolbox-will-mount} :init icons-toolbox-init}
[] []
(let [drawing (mx/react drawing-shape-ref) (let [drawing (mx/react drawing-shape-ref)
selected (mx/react icons-toolbox-ref) selected (mx/react icons-toolbox-ref)

View file

@ -89,43 +89,42 @@
;; --- Shape Name (Component) ;; --- Shape Name (Component)
(mx/defcs shape-name (mx/def shape-name
"A generic component that displays the shape name :mixins [mx/static (mx/local)]
if it is available and allows inline edition of it." :render
{:mixins [mx/static (mx/local)]} (fn [{:keys [::mx/local] :as own} {:keys [id] :as shape}]
[{:keys [rum/local]} {:keys [id] :as shape}] (letfn [(on-blur [event]
(letfn [(on-blur [event] (let [target (dom/event->target event)
(let [target (dom/event->target event) parent (.-parentNode target)
parent (.-parentNode target) name (dom/get-value target)]
name (dom/get-value target)] (set! (.-draggable parent) true)
(set! (.-draggable parent) true) (st/emit! (uds/rename-shape id name))
(st/emit! (uds/rename-shape id name)) (swap! local assoc :edition false)))
(swap! local assoc :edition false))) (on-key-down [event]
(on-key-down [event] (js/console.log event)
(js/console.log event) (when (kbd/enter? event)
(when (kbd/enter? event) (on-blur event)))
(on-blur event))) (on-click [event]
(on-click [event] (dom/prevent-default event)
(dom/prevent-default event) (let [parent (.-parentNode (.-target event))]
(let [parent (.-parentNode (.-target event))] (set! (.-draggable parent) false))
(set! (.-draggable parent) false)) (swap! local assoc :edition true))]
(swap! local assoc :edition true))] (if (:edition @local)
(if (:edition @local) [:input.element-name
[:input.element-name {:type "text"
{:type "text" :on-blur on-blur
:on-blur on-blur :on-key-down on-key-down
:on-key-down on-key-down :auto-focus true
:auto-focus true :default-value (:name shape "")}]
:default-value (:name shape "")}] [:span.element-name
[:span.element-name {:on-double-click on-click}
{:on-double-click on-click} (:name shape "")]))))
(:name shape "")])))
;; --- Layer Simple (Component) ;; --- Layer Simple (Component)
(mx/defcs layer-simple (mx/defcs layer-simple
{:mixins [mx/static (mx/local)]} {:mixins [mx/static (mx/local)]}
[{:keys [rum/local]} item selected] [{:keys [::mx/local]} item selected]
(let [selected? (contains? selected (:id item)) (let [selected? (contains? selected (:id item))
select #(select-shape selected item %) select #(select-shape selected item %)
toggle-visibility #(toggle-visibility selected item %) toggle-visibility #(toggle-visibility selected item %)
@ -189,14 +188,14 @@
{:class (when (:blocked item) "selected") {:class (when (:blocked item) "selected")
:on-click toggle-blocking} :on-click toggle-blocking}
i/lock]] i/lock]]
[:div.element-icon {} (element-icon item)] [:div.element-icon (element-icon item)]
(shape-name item)]]))) (shape-name item)]])))
;; --- Layer Group (Component) ;; --- Layer Group (Component)
(mx/defcs layer-group (mx/defcs layer-group
{:mixins [mx/static mx/reactive (mx/local)]} {:mixins [mx/static mx/reactive (mx/local)]}
[{:keys [rum/local]} {:keys [id] :as item} selected] [{:keys [::mx/local]} {:keys [id] :as item} selected]
(let [selected? (contains? selected (:id item)) (let [selected? (contains? selected (:id item))
collapsed? (:collapsed item true) collapsed? (:collapsed item true)
shapes-map (mx/react refs/shapes-by-id) shapes-map (mx/react refs/shapes-by-id)

View file

@ -8,27 +8,27 @@
(ns uxbox.main.ui.workspace.sidebar.options (ns uxbox.main.ui.workspace.sidebar.options
(:require (:require
[lentes.core :as l] [lentes.core :as l]
[uxbox.util.i18n :refer [tr]]
[uxbox.util.router :as r]
[potok.core :as ptk] [potok.core :as ptk]
[uxbox.main.store :as st] [rumext.core :as mx :include-macros true]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.shapes :as uds]
[uxbox.builtins.icons :as i] [uxbox.builtins.icons :as i]
[uxbox.main.data.shapes :as uds]
[uxbox.main.data.workspace :as udw]
[uxbox.main.geom :as geom]
[uxbox.main.store :as st]
[uxbox.main.ui.shapes.attrs :refer [shape-default-attrs]] [uxbox.main.ui.shapes.attrs :refer [shape-default-attrs]]
[uxbox.main.ui.workspace.sidebar.options.circle-measures :as options-circlem]
[uxbox.main.ui.workspace.sidebar.options.fill :as options-fill]
[uxbox.main.ui.workspace.sidebar.options.icon-measures :as options-iconm] [uxbox.main.ui.workspace.sidebar.options.icon-measures :as options-iconm]
[uxbox.main.ui.workspace.sidebar.options.image-measures :as options-imagem] [uxbox.main.ui.workspace.sidebar.options.image-measures :as options-imagem]
[uxbox.main.ui.workspace.sidebar.options.circle-measures :as options-circlem]
[uxbox.main.ui.workspace.sidebar.options.rect-measures :as options-rectm]
[uxbox.main.ui.workspace.sidebar.options.fill :as options-fill]
[uxbox.main.ui.workspace.sidebar.options.text :as options-text]
[uxbox.main.ui.workspace.sidebar.options.stroke :as options-stroke]
[uxbox.main.ui.workspace.sidebar.options.page :as options-page]
[uxbox.main.ui.workspace.sidebar.options.interactions :as options-interactions] [uxbox.main.ui.workspace.sidebar.options.interactions :as options-interactions]
[uxbox.main.geom :as geom] [uxbox.main.ui.workspace.sidebar.options.page :as options-page]
[uxbox.util.dom :as dom] [uxbox.main.ui.workspace.sidebar.options.rect-measures :as options-rectm]
[uxbox.main.ui.workspace.sidebar.options.stroke :as options-stroke]
[uxbox.main.ui.workspace.sidebar.options.text :as options-text]
[uxbox.util.data :as data] [uxbox.util.data :as data]
[rumext.core :as mx :include-macros true])) [uxbox.util.dom :as dom]
[uxbox.util.i18n :refer [tr]]
[uxbox.util.router :as r]))
;; --- Constants ;; --- Constants
@ -89,18 +89,10 @@
;; --- Options ;; --- Options
(defn- options-did-remount
[old-own own]
(let [[prev-shape] (:rum/args old-own)
[curr-shape] (:rum/args own)]
(when-not (= (:id prev-shape) (:id curr-shape))
(reset! (:rum/local own) {}))
own))
(mx/defcs options (mx/defcs options
{:mixins [mx/static (mx/local)] {:mixins [mx/static (mx/local)]
:did-remount options-did-remount} :key-fn #(pr-str (:id %1))}
[{:keys [rum/local] :as own} shape] [{:keys [::mx/local] :as own} shape]
(let [menus (get +menus-map+ (:type shape ::page)) (let [menus (get +menus-map+ (:type shape ::page))
contained-in? (into #{} menus) contained-in? (into #{} menus)
active (:menu @local (first menus))] active (:menu @local (first menus))]

View file

@ -2,24 +2,25 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; 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> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.workspace.sidebar.options.interactions (ns uxbox.main.ui.workspace.sidebar.options.interactions
(:require [lentes.core :as l] (:require
[uxbox.builtins.icons :as i] [lentes.core :as l]
[uxbox.util.i18n :refer [tr]] [rumext.core :as mx :include-macros true]
[uxbox.util.router :as r] [uxbox.builtins.icons :as i]
[uxbox.main.refs :as refs] [uxbox.main.data.lightbox :as udl]
[uxbox.main.store :as st] [uxbox.main.data.shapes :as uds]
[uxbox.main.data.shapes :as uds] [uxbox.main.refs :as refs]
[uxbox.main.data.lightbox :as udl] [uxbox.main.store :as st]
[uxbox.main.ui.lightbox :as lbx] [uxbox.main.ui.colorpicker :as cp]
[uxbox.main.ui.colorpicker :as cp] [uxbox.main.ui.lightbox :as lbx]
[uxbox.util.dom :as dom] [uxbox.util.data :refer [read-string]]
[uxbox.util.data :refer [read-string]] [uxbox.util.dom :as dom]
[uxbox.util.spec :refer [color?]] [uxbox.util.i18n :refer [tr]]
[rumext.core :as mx :include-macros true])) [uxbox.util.router :as r]
[uxbox.util.spec :refer [color?]]))
;; --- Helpers ;; --- Helpers
@ -473,7 +474,7 @@
(mx/defcs interactions-menu (mx/defcs interactions-menu
{:mixins [mx/static (mx/local)]} {:mixins [mx/static (mx/local)]}
[own menu shape] [own menu shape]
(let [local (:rum/local own) (let [local (::mx/local own)
form-ref (l/derive (l/key :form) local) form-ref (l/derive (l/key :form) local)
interactions (:interactions shape) interactions (:interactions shape)
create-interaction #(reset! form-ref {})] create-interaction #(reset! form-ref {})]

View file

@ -22,7 +22,7 @@
(mx/defcs stroke-menu (mx/defcs stroke-menu
{:mixins [mx/static (mx/local)]} {:mixins [mx/static (mx/local)]}
[{:keys [rum/local]} menu {:keys [id] :as shape}] [{:keys [::mx/local]} menu {:keys [id] :as shape}]
(letfn [(on-width-change [event] (letfn [(on-width-change [event]
(let [value (-> (dom/event->value event) (let [value (-> (dom/event->value event)
(parse-float 1))] (parse-float 1))]

View file

@ -6,119 +6,126 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.sitemap (ns uxbox.main.ui.workspace.sidebar.sitemap
(:require [lentes.core :as l] (:require
[cuerdas.core :as str] [cuerdas.core :as str]
[uxbox.builtins.icons :as i] [lentes.core :as l]
[uxbox.main.store :as st] [rumext.core :as mx :include-macros true]
[uxbox.main.refs :as refs] [uxbox.builtins.icons :as i]
[uxbox.main.data.projects :as dp] [uxbox.main.data.lightbox :as udl]
[uxbox.main.data.pages :as udp] [uxbox.main.data.pages :as udp]
[uxbox.main.data.workspace :as dw] [uxbox.main.data.projects :as dp]
[uxbox.main.data.lightbox :as udl] [uxbox.main.data.workspace :as dw]
[uxbox.main.ui.workspace.sidebar.sitemap-pageform] [uxbox.main.refs :as refs]
[uxbox.main.ui.lightbox :as lbx] [uxbox.main.store :as st]
[uxbox.util.i18n :refer (tr)] [uxbox.main.ui.lightbox :as lbx]
[uxbox.util.router :as r] [uxbox.main.ui.workspace.sidebar.sitemap-pageform]
[uxbox.util.data :refer [classnames]] [uxbox.util.data :refer [classnames]]
[uxbox.util.dom.dnd :as dnd] [uxbox.util.dom :as dom]
[uxbox.util.dom :as dom] [uxbox.util.dom.dnd :as dnd]
[rumext.core :as mx :include-macros true])) [uxbox.util.i18n :refer (tr)]
[uxbox.util.router :as r]))
(mx/defcs page-item (mx/def page-item
{:mixins [(mx/local) mx/static mx/reactive]} :mixins [(mx/local) mx/static mx/reactive]
[{:keys [rum/local] :as own} page total active?] :key-fn :id
(let [body-classes (classnames
:selected active?
:drag-active (:dragging @local)
:drag-top (= :top (:over @local))
:drag-bottom (= :bottom (:over @local))
:drag-inside (= :middle (:over @local)))
li-classes (classnames
:selected active?
:hide (:dragging @local))]
(letfn [(on-edit [event]
(udl/open! :page-form {:page page}))
(on-navigate [event] :render
(st/emit! (dp/go-to (:project page) (:id page)))) (fn [{:keys [::mx/local] :as own}
{:keys [::deletable? ::selected?] :as page}]
(let [body-classes (classnames
:selected selected?
:drag-active (:dragging @local)
:drag-top (= :top (:over @local))
:drag-bottom (= :bottom (:over @local))
:drag-inside (= :middle (:over @local)))
li-classes (classnames
:selected selected?
:hide (:dragging @local))]
(letfn [(on-edit [event]
(udl/open! :page-form {:page page}))
(delete [] (on-navigate [event]
(let [next #(st/emit! (dp/go-to (:project page)))] (st/emit! (dp/go-to (:project page) (:id page))))
(st/emit! (udp/delete-page (:id page) next))))
(on-delete [event] (delete []
(dom/prevent-default event) (let [next #(st/emit! (dp/go-to (:project page)))]
(dom/stop-propagation event) (st/emit! (udp/delete-page (:id page) next))))
(udl/open! :confirm {:on-accept delete}))
(on-drag-start [event] (on-delete [event]
(let [target (dom/event->target event)] (dom/prevent-default event)
(dnd/set-allowed-effect! event "move") (dom/stop-propagation event)
(dnd/set-data! event (:id page)) (udl/open! :confirm {:on-accept delete}))
(dnd/set-image! event target 50 10)
(swap! local assoc :dragging true)))
(on-drag-end [event]
(swap! local assoc :dragging false :over nil))
(on-drop [event]
(dom/stop-propagation event)
(let [id (dnd/get-data event)
over (:over @local)]
(case (:over @local)
:top (let [new-order (dec (get-in page [:metadata :order]))]
(st/emit! (udp/update-order id new-order)))
:bottom (let [new-order (inc (get-in page [:metadata :order]))]
(st/emit! (udp/update-order id new-order))))
(swap! local assoc :dragging false :over nil)))
(on-drag-over [event]
(dom/prevent-default event)
(dnd/set-drop-effect! event "move")
(let [over (dnd/get-hover-position event false)]
(swap! local assoc :over over)))
(on-drag-enter [event]
(swap! local assoc :over true))
(on-drag-leave [event]
(swap! local assoc :over false))]
[:li {:class li-classes}
[:div.element-list-body
{:class body-classes
:style {:opacity (if (:dragging @local)
"0.5"
"1")}
:on-click on-navigate
:on-double-click #(dom/stop-propagation %)
:on-drag-start on-drag-start
:on-drag-enter on-drag-enter
:on-drag-leave on-drag-leave
:on-drag-over on-drag-over
:on-drag-end on-drag-end
:on-drop on-drop
:draggable true}
[:div.page-icon {} i/page] (on-drag-start [event]
[:span {} (:name page)] (let [target (dom/event->target event)]
[:div.page-actions {} (dnd/set-allowed-effect! event "move")
[:a {:on-click on-edit} i/pencil] (dnd/set-data! event (:id page))
(when (> total 1) (dnd/set-image! event target 50 10)
[:a {:on-click on-delete} i/trash])]]]))) (swap! local assoc :dragging true)))
(on-drag-end [event]
(swap! local assoc :dragging false :over nil))
(on-drop [event]
(dom/stop-propagation event)
(let [id (dnd/get-data event)
over (:over @local)]
(case (:over @local)
:top (let [new-order (dec (get-in page [:metadata :order]))]
(st/emit! (udp/update-order id new-order)))
:bottom (let [new-order (inc (get-in page [:metadata :order]))]
(st/emit! (udp/update-order id new-order))))
(swap! local assoc :dragging false :over nil)))
(on-drag-over [event]
(dom/prevent-default event)
(dnd/set-drop-effect! event "move")
(let [over (dnd/get-hover-position event false)]
(swap! local assoc :over over)))
(on-drag-enter [event]
(swap! local assoc :over true))
(on-drag-leave [event]
(swap! local assoc :over false))]
[:li {:class li-classes}
[:div.element-list-body
{:class body-classes
:style {:opacity (if (:dragging @local)
"0.5"
"1")}
:on-click on-navigate
:on-double-click #(dom/stop-propagation %)
:on-drag-start on-drag-start
:on-drag-enter on-drag-enter
:on-drag-leave on-drag-leave
:on-drag-over on-drag-over
:on-drag-end on-drag-end
:on-drop on-drop
:draggable true}
(mx/defc sitemap-toolbox [:div.page-icon {} i/page]
{:mixins [mx/static mx/reactive]} [:span {} (:name page)]
[current] [:div.page-actions {}
(let [project (mx/react refs/selected-project) [:a {:on-click on-edit} i/pencil]
pages (mx/react refs/selected-project-pages) (when deletable?
create #(udl/open! :page-form {:page {:project (:id project)}}) [:a {:on-click on-delete} i/trash])]]]))))
close #(st/emit! (dw/toggle-flag :sitemap))]
[:div.sitemap.tool-window {} (mx/def sitemap-toolbox
[:div.tool-window-bar {} :mixins [mx/static mx/reactive]
[:div.tool-window-icon {} i/project-tree]
[:span {} (tr "ds.sitemap")] :render
[:div.tool-window-close {:on-click close} i/close]] (fn [own current-page-id]
[:div.tool-window-content {} (let [project (mx/react refs/selected-project)
[:div.project-title {} pages (mx/react refs/selected-project-pages)
[:span {} (:name project)] create #(udl/open! :page-form {:page {:project (:id project)}})
[:div.add-page {:on-click create} i/close]] close #(st/emit! (dw/toggle-flag :sitemap))
[:ul.element-list {} deletable? (> (count pages) 1)]
(for [page pages] [:div.sitemap.tool-window
(let [active? (= (:id page) current)] [:div.tool-window-bar
(-> (page-item page (count pages) active?) [:div.tool-window-icon i/project-tree]
(mx/with-key (:id page)))))]]])) [:span (tr "ds.sitemap")]
[:div.tool-window-close {:on-click close} i/close]]
[:div.tool-window-content
[:div.project-title
[:span (:name project)]
[:div.add-page {:on-click create} i/close]]
[:ul.element-list
(for [page pages]
(let [selected? (= (:id page) current-page-id)]
(page-item (assoc page ::deletable? deletable? ::selected? selected?))))]]])))