This commit is contained in:
Andrey Antukh 2015-12-14 20:31:21 +02:00
parent 56f7613453
commit 6b6aba7358
8 changed files with 258 additions and 37 deletions

View file

@ -1,32 +1,104 @@
(ns uxbox.data.projects (ns uxbox.data.projects
(:require [uxbox.rstore :as rs] (:require [uxbox.rstore :as rs]
[uxbox.router :as r]
[uxbox.state :as st] [uxbox.state :as st]
[uxbox.schema :as sc] [uxbox.schema :as sc]
[bouncer.validators :as v])) [bouncer.validators :as v]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Schemas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:static +project-schema+ (def ^:static +project-schema+
{:name [v/required v/string] {:name [v/required v/string]
:width [v/required v/integer] :width [v/required v/integer]
:height [v/required v/integer] :height [v/required v/integer]
:layout [v/required sc/keyword?]}) :layout [v/required sc/keyword]})
(defn create-project (def ^:static +page-schema+
[{:keys [name width height layout] :as data}] {:name [v/required v/string]
(sc/validate! +project-schema+ data) :width [v/required v/integer]
(println "create-project") :height [v/required v/integer]
:project [v/required sc/uuid]})
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn create-page
[{:keys [name width height project] :as data}]
(sc/validate! +page-schema+ data)
(reify (reify
rs/UpdateEvent rs/UpdateEvent
(-apply-update [_ state] (-apply-update [_ state]
(let [uuid (random-uuid) (let [uuid (random-uuid)
proj {:id uuid page {:id uuid
:project project
:name name :name name
:width width :width width
:height height :height height}]
:pages []}]
(-> state (-> state
(update-in [:projects] conj uuid) (update-in [:pages] conj uuid)
(update-in [:projects-by-id] assoc uuid {:name name})))) (update-in [:projects-by-id project :pages] conj uuid)
(update-in [:pages-by-id] assoc uuid page))))
IPrintWithWriter IPrintWithWriter
(-pr-writer [mv writer _] (-pr-writer [mv writer _]
(-write writer "#<event:u.s.p/create-project>")))) (-write writer "#<event:u.s.p/create-page>"))))
(defn create-project
[{:keys [name width height layout] :as data}]
(sc/validate! +project-schema+ data)
(let [uuid (random-uuid)]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(let [proj {:id uuid
:name name
:width width
:height height
:pages []}]
(-> state
(update-in [:projects] conj uuid)
(update-in [:projects-by-id] assoc uuid proj))))
rs/EffectEvent
(-apply-effect [_ state]
(rs/emit! (create-page {:name "Page 1"
:width width
:height height
:project uuid})))
IPrintWithWriter
(-pr-writer [mv writer _]
(-write writer "#<event:u.s.p/create-project>")))))
(defn initialize-workspace
[projectid pageid]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(assoc state :workspace {:project projectid :page pageid}))
IPrintWithWriter
(-pr-writer [mv writer _]
(-write writer "#<event:u.s.p/go-to-project>"))))
(defn go-to-project
"A shortcut event that redirects the user to the
first page of the project."
([projectid]
(go-to-project projectid nil))
([projectid pageid]
(reify
rs/EffectEvent
(-apply-effect [_ state]
(if pageid
(rs/emit! (r/navigate :main/page {:project-uuid projectid
:page-uuid pageid}))
(let [pages (get-in state [:projects-by-id projectid :pages])]
(rs/emit! (r/navigate :main/page {:project-uuid projectid
:page-uuid (first pages)})))))
IPrintWithWriter
(-pr-writer [mv writer _]
(-write writer "#<event:u.s.p/go-to-project")))))

View file

@ -43,6 +43,7 @@
(let [loc (merge {:handler name} (let [loc (merge {:handler name}
(when params (when params
{:route-params params}))] {:route-params params}))]
(println "navigate" loc)
(bidi.router/set-location! +router+ loc)))))) (bidi.router/set-location! +router+ loc))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -50,7 +51,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private project-route (def ^:private project-route
[bidi/uuid :project-uuid]) [[bidi/uuid :project-uuid]])
(def ^:private page-route (def ^:private page-route
[[bidi/uuid :project-uuid] "/" [bidi/uuid :page-uuid]]) [[bidi/uuid :project-uuid] "/" [bidi/uuid :page-uuid]])

View file

@ -1,5 +1,5 @@
(ns uxbox.schema (ns uxbox.schema
(:refer-clojure :exclude [keyword?]) (:refer-clojure :exclude [keyword uuid])
(:require [bouncer.core :as b] (:require [bouncer.core :as b]
[bouncer.validators :as v])) [bouncer.validators :as v]))
@ -7,13 +7,21 @@
;; Validators ;; Validators
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(v/defvalidator keyword? (v/defvalidator keyword
"Validates maybe-an-int is a valid integer. "Validates maybe-an-int is a valid integer.
For use with validation functions such as `validate` or `valid?`" For use with validation functions such as `validate` or `valid?`"
{:default-message-format "%s must be a keyword"} {:default-message-format "%s must be a keyword"}
[v] [v]
(cljs.core/keyword? v)) (cljs.core/keyword? v))
(v/defvalidator uuid
"Validates maybe-an-int is a valid integer.
For use with validation functions such as `validate` or `valid?`"
{:default-message-format "%s must be a uuid instance"}
[v]
(instance? cljs.core.UUID v))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Public Api ;; Public Api
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -9,6 +9,7 @@
(defonce stream (defonce stream
(rs/init {:user {:fullname "Cirilla" (rs/init {:user {:fullname "Cirilla"
:avatar "http://lorempixel.com/50/50/"} :avatar "http://lorempixel.com/50/50/"}
:workspace nil
:projects [] :projects []
:pages [] :pages []
:projects-by-id {} :projects-by-id {}
@ -16,5 +17,6 @@
(defonce +setup-stuff+ (defonce +setup-stuff+
(do (do
(rx/to-atom stream state))) (rx/to-atom stream state)
(rx/on-value stream #(println "state:" %))))

View file

@ -3,9 +3,12 @@
[rum.core :as rum] [rum.core :as rum]
[cats.labs.lens :as l] [cats.labs.lens :as l]
[uxbox.state :as s] [uxbox.state :as s]
[uxbox.rstore :as rs]
[uxbox.util :as util] [uxbox.util :as util]
[uxbox.data.projects :as dp]
[uxbox.ui.lightbox :as ui.lb] [uxbox.ui.lightbox :as ui.lb]
[uxbox.ui.users :as ui.u] [uxbox.ui.users :as ui.u]
[uxbox.ui.workspace :as ui.w]
[uxbox.ui.dashboard :as ui.d])) [uxbox.ui.dashboard :as ui.d]))
(def ^:static state (def ^:static state
@ -15,6 +18,7 @@
(defn app-render (defn app-render
[own] [own]
(let [{:keys [location location-params] :as state} (rum/react state)] (let [{:keys [location location-params] :as state} (rum/react state)]
(println 1111 location location-params)
(html (html
[:section [:section
(ui.lb/lightbox) (ui.lb/lightbox)
@ -23,8 +27,11 @@
;; :auth/register (u/register) ;; :auth/register (u/register)
;; :auth/recover (u/recover-password) ;; :auth/recover (u/recover-password)
:main/dashboard (ui.d/dashboard) :main/dashboard (ui.d/dashboard)
;; :main/project (w/workspace conn location-params) ;; :main/project (ui.w/workspace (:project-uuid location-params))
;; :main/page (w/workspace conn location-params)))) :main/page (let [projectid (:project-uuid location-params)
pageid (:page-uuid location-params)]
(rs/emit! (dp/initialize-workspace projectid pageid))
(ui.w/workspace projectid pageid))
nil nil
)]))) )])))

View file

@ -197,25 +197,27 @@
(defn project-render (defn project-render
[own project] [own project]
(html (println "project-render" project)
[:div.grid-item.project-th {:on-click (constantly nil) (let [on-click #(rs/emit! (dp/go-to-project (:id project)))]
:key (:uuid project)} (html
[:h3 (:name project)] [:div.grid-item.project-th {:on-click on-click
[:span.project-th-update :key (:id project)}
(str "Updated " (ago (:last-update project)))] [:h3 (:name project)]
[:div.project-th-actions [:span.project-th-update
[:div.project-th-icon.pages (str "Updated " (ago (:last-update project)))]
icons/page [:div.project-th-actions
[:span 0]] [:div.project-th-icon.pages
[:div.project-th-icon.comments icons/page
i/chat [:span 0]]
[:span 0]] [:div.project-th-icon.comments
[:div.project-th-icon.delete i/chat
{:on-click #(do [:span 0]]
(dom/stop-propagation %) [:div.project-th-icon.delete
;; (actions/delete-project conn uuid) {:on-click #(do
%)} (dom/stop-propagation %)
icons/trash]]])) ;; (actions/delete-project conn uuid)
%)}
icons/trash]]])))
(def project-item (def project-item
(util/component (util/component

View file

@ -0,0 +1,118 @@
(ns uxbox.ui.workspace
(:require [sablono.core :as html :refer-macros [html]]
[rum.core :as rum]
[cats.labs.lens :as l]
[cuerdas.core :as str]
[uxbox.util :as util]
[uxbox.router :as r]
[uxbox.rstore :as rs]
[uxbox.state :as s]
[uxbox.data.projects :as dp]
[uxbox.ui.icons.dashboard :as icons]
[uxbox.ui.icons :as i]
[uxbox.ui.dom :as dom]
[uxbox.ui.header :as ui.h]
[uxbox.ui.lightbox :as lightbox]
[uxbox.time :refer [ago]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Header
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (defn header-render
;; [conn page grid? project-bar-visible?]
;; (let [{page-title :page/title
;; page-width :page/width
;; page-height :page/height} page]
;; [:header#workspace-bar.workspace-bar
;; [:div.main-icon
;; (nav/link (nav/route-for :dashboard) icons/logo-icon)]
;; (project-tree page-title project-bar-visible?)
;; [:div.workspace-options
;; [:ul.options-btn
;; [:li.tooltip.tooltip-bottom {:alt "Undo (Ctrl + Z)"}
;; icons/undo]
;; [:li.tooltip.tooltip-bottom {:alt "Redo (Ctrl + Shift + Z)"}
;; icons/redo]]
;; [:ul.options-btn
;; ;; TODO: refactor
;; [:li.tooltip.tooltip-bottom
;; {:alt "Export (Ctrl + E)"}
;; ;; page-title
;; [:a {:download (str page-title ".svg")
;; :href "#"
;; :on-click (fn [e]
;; (let [innerHTML (.-innerHTML (.getElementById js/document "page-layout"))
;; width page-width
;; height page-height
;; html (str "<svg width='" width "' height='" height "'>" innerHTML "</svg>")
;; data (js/Blob. #js [html] #js {:type "application/octet-stream"})
;; url (.createObjectURL (.-URL js/window) data)]
;; (set! (.-href (.-currentTarget e)) url)))}
;; icons/export]]
;; [:li.tooltip.tooltip-bottom
;; {:alt "Image (Ctrl + I)"}
;; icons/image]]
;; [:ul.options-btn
;; [:li.tooltip.tooltip-bottom
;; {:alt "Ruler (Ctrl + R)"}
;; icons/ruler]
;; [:li.tooltip.tooltip-bottom
;; {:alt "Grid (Ctrl + G)"
;; :class (when (rum/react ws/grid?) "selected")
;; :on-click ws/toggle-grid!}
;; icons/grid]
;; [:li.tooltip.tooltip-bottom
;; {:alt "Align (Ctrl + A)"}
;; icons/alignment]
;; [:li.tooltip.tooltip-bottom
;; {:alt "Organize (Ctrl + O)"}
;; icons/organize]]]
;; (user conn)]))
;; (def header
;; (util/component
;; {:render header-render
;; :name "header"}))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Header
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def project-state
(as-> (util/dep-in [:projects-by-id] [:workspace :project]) $
(l/focus-atom $ s/state)))
(def page-state
(as-> (util/dep-in [:pages-by-id] [:workspace :page]) $
(l/focus-atom $ s/state)))
(defn workspace-render
[own projectid]
(println "workspace-render" @s/state)
(println 22222 @page-state)
(html
[:div "hello world"
;; (header conn page ws/grid? project-bar-visible?)
;; [:main.main-content
;; [:section.workspace-content
;; ;; Toolbar
;; (toolbar open-toolboxes)
;; ;; Project bar
;; (project-bar conn project page pages @project-bar-visible?)
;; ;; Rules
;; (horizontal-rule (rum/react ws/zoom))
;; (vertical-rule (rum/react ws/zoom))
;; ;; Working area
;; (working-area conn @open-toolboxes page project shapes (rum/react ws/zoom) (rum/react ws/grid?))
;; ;; Aside
;; (when-not (empty? @open-toolboxes)
;; (aside conn open-toolboxes page shapes))
]))
(def ^:static workspace
(util/component
{:render workspace-render
:name "workspace"
:mixins [rum/static]}))

View file

@ -112,9 +112,20 @@
})) }))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Lenses Helpers ;; Lenses & Helpers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn dep-in
[where link]
{:pre [(vector? where) (vector? link)]}
(l/lens
(fn [s]
(let [value (get-in s link)
path (conj where value)]
(get-in s path)))
(fn [s f]
(throw (ex-info "Not implemented" {})))))
(defn derive (defn derive
[a path] [a path]
(l/focus-atom (l/in path) a)) (l/focus-atom (l/in path) a))