diff --git a/frontend/uxbox/ui.cljs b/frontend/uxbox/ui.cljs index 29cd53d22d..cefa94a08b 100644 --- a/frontend/uxbox/ui.cljs +++ b/frontend/uxbox/ui.cljs @@ -13,15 +13,18 @@ (defn app-render [own] (let [{:keys [location location-params] :as state} (rum/react state)] - (case location - :auth/login (ui.u/login) - ;; :auth/register (u/register) - ;; :auth/recover (u/recover-password) - :main/dashboard (ui.d/dashboard) - ;; :main/project (w/workspace conn location-params) - ;; :main/page (w/workspace conn location-params)))) - nil - ))) + (html + [:section + (lb/lightb + (case location + :auth/login (ui.u/login) + ;; :auth/register (u/register) + ;; :auth/recover (u/recover-password) + :main/dashboard (ui.d/dashboard) + ;; :main/project (w/workspace conn location-params) + ;; :main/page (w/workspace conn location-params)))) + nil + ))) (def app (util/component {:render app-render diff --git a/frontend/uxbox/ui/dashboard.cljs b/frontend/uxbox/ui/dashboard.cljs index 2fd44ce7db..dc4d6badfe 100644 --- a/frontend/uxbox/ui/dashboard.cljs +++ b/frontend/uxbox/ui/dashboard.cljs @@ -4,25 +4,16 @@ [cuerdas.core :refer [trim]] [uxbox.util :as util] [uxbox.router :as r] - [uxbox.ui.navigation :as nav] - ;; [uxbox.ui.mixins :as mx] [uxbox.ui.icons :as i] - [uxbox.ui.users :as ui.u] - ;; [uxbox.ui.lightbox :refer [lightbox - ;; render-lightbox - ;; set-lightbox! - ;; close-lightbox!]] + [uxbox.ui.dom :as dom] + [uxbox.ui.header :as ui.h] + [uxbox.ui.lightbox :as lightbox] ;; [uxbox.ui.activity :refer [timeline]] [uxbox.ui.icons.dashboard :as icons] ;; [uxbox.projects.queries :as q] ;; [uxbox.projects.actions :as actions] [uxbox.time :refer [ago]])) -(def lightbox (constantly nil)) -(def render-lightbox (constantly nil)) -(def set-lightbox! (constantly nil)) -(def close-lightbox! (constantly nil)) - ;; Config ;; TODO: i18nized names (def project-orderings {:project/name "name" @@ -55,51 +46,52 @@ ;; Views +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Lightbox +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn layout-input - [layout new-project] + [layout local] (let [human-name (get-in project-layouts [layout :name]) id (str "project-" (get-in project-layouts [layout :id])) tag (str "input#" id) tag (keyword tag)] [[tag - {:type "radio" - :key id - :name "project-layout" - :value human-name - :checked (= layout (:layout @new-project)) - :on-change #(swap! new-project merge {:layout layout - :width (get-in project-layouts [layout :width]) - :height (get-in project-layouts [layout :height])})}] - [:label - {:value name - :for id} - human-name]])) + {:type "radio" + :key id + :name "project-layout" + :value human-name + :checked (= layout (:layout @local)) + :on-change #(swap! local merge {:layout layout + :width (get-in project-layouts [layout :width]) + :height (get-in project-layouts [layout :height])})}] + [:label + {:value name + :for id} + human-name]])) -(rum/defc layout-selector - [new-project] - (vec (cons :div.input-radio.radio-primary - (mapcat #(layout-input % new-project) (keys project-layouts))))) +(defn- layout-selector + [local] + (html + [:div.input-radio.radio-primary + (vec (cons :div.input-radio.radio-primary + (mapcat #(layout-input % local) (keys project-layouts))))])) -(rum/defcs new-project-lightbox < (rum/local new-project-defaults :new-project) - [{:keys [new-project]} conn] - (let [{:keys [name width height layout]} @new-project] +(defn- new-project-lightbox-render + [own] + (let [local (:rum/local own) + width (:width @local) + height (:height @local)] + (html [:div.lightbox-body [:h3 "New project"] - [:form - {:on-submit (fn [e] - (.preventDefault e) - (let [new-project-attributes {:name (trim name) - :width (int width) - :height (int height) - :layout layout}] - ;; (actions/create-project conn new-project-attributes) - (close-lightbox!)))} + [:form {:on-submit (constantly nil)} [:input#project-name.input-text - {:placeholder "New project name" - :type "text" - :value name - :auto-focus true - :on-change #(swap! new-project assoc :name (.-value (.-target %)))}] + {:placeholder "New project name" + :type "text" + :value name + :auto-focus true + :on-change #(swap! local assoc :name (.-value (.-target %)))}] [:div.project-size [:input#project-witdh.input-text {:placeholder "Width" @@ -107,10 +99,10 @@ :min 0 ;;TODO check this value :max 666666 ;;TODO check this value :value width - :on-change #(swap! new-project assoc :width (.-value (.-target %)))}] + :on-change #(swap! local assoc :width (.-value (.-target %)))}] [:a.toggle-layout {:href "#" - :on-click #(swap! new-project assoc :width height :height width)} + :on-click #(swap! local assoc :width width :height height)} i/toggle] [:input#project-height.input-text {:placeholder "Height" @@ -118,9 +110,9 @@ :min 0 ;;TODO check this value :max 666666 ;;TODO check this value :value height - :on-change #(swap! new-project assoc :height (.-value (.-target %)))}]] + :on-change #(swap! local assoc :height (.-value (.-target %)))}]] ;; Layout selector - (layout-selector new-project) + (layout-selector local) ;; Submit (when-not (empty? (trim name)) [:input#project-btn.btn-primary @@ -128,23 +120,30 @@ :type "submit"}])] [:a.close {:href "#" - :on-click #(close-lightbox!)} - i/close]])) + :on-click #(lightbox/close!)} + i/close]]))) -;; (defmethod render-lightbox :new-project -;; [_ conn] -;; (new-project-lightbox conn)) + ;; (.preventDefault e) + ;; (let [new-project-attributes {:name (trim name) + ;; :width (int width) + ;; :height (int height) + ;; :layout layout}] + ;; ;; (actions/create-project conn new-project-attributes) + ;; (close-lightbox!)))} -(rum/defc header - [] - [:header#main-bar.main-bar - [:div.main-logo - (nav/link "/" i/logo)] - (ui.u/user)]) +(def new-project-lightbox + (util/component + {:render new-project-lightbox-render + :name "new-project-lightbox" + :mixins [(rum/local new-project-defaults)]})) -(rum/defc project-count < rum/static - [n] - [:span.dashboard-projects n " projects"]) +(defmethod lightbox/render-lightbox :new-project + [_] + (new-project-lightbox)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Menu +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (rum/defc project-sort-selector < rum/reactive [sort-order] @@ -156,47 +155,57 @@ :let [name (get project-orderings order)]] [:option {:key name} name])])) -(rum/defc dashboard-bar +(rum/defc project-count < rum/static + [n] + [:span.dashboard-projects n " projects"]) + +(defn menu-render [] - [:section#dashboard-bar.dashboard-bar + (html + [:section#dashboard-bar.dashboard-bar [:div.dashboard-info (project-count 0) [:span "Sort by"] (project-sort-selector (atom :name))] [:div.dashboard-search - icons/search]]) + icons/search]])) -(rum/defc project-card < rum/static - [conn - {uuid :project/uuid - last-update :project/last-updated - name :project/name - pages :project/pages - comment-count :project/comment-count}] - [:div.grid-item.project-th - {:on-click #(r/go :project {:project-uuid uuid}) - :key uuid} - [:h3 - name] - [:span.project-th-update "Updated " (ago last-update)] - [:div.project-th-actions - [:div.project-th-icon.pages - icons/page - [:span pages]] - [:div.project-th-icon.comments - i/chat - [:span comment-count]] - [:div.project-th-icon.delete - {:on-click #(do (.stopPropagation %) - ;; (actions/delete-project conn uuid) - %)} - icons/trash]]]) +(def menu + (util/component + {:render menu-render + :name "dashboard-menu"})) -(rum/defc new-project < rum/static - [] - [:div.grid-item.add-project - {:on-click #(set-lightbox! :new-project)} - [:span "+ New project"]]) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Project Item +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn project-render + [own project] + (html + [:div.grid-item.project-th {:on-click (constantly nil) + :key (:uuid project)} + [:h3 (:name project)] + [:span.project-th-update + (str "Updated " (ago (:last-update project)))] + [:div.project-th-actions + [:div.project-th-icon.pages + icons/page + [:span 0]] + [:div.project-th-icon.comments + i/chat + [:span 0]] + [:div.project-th-icon.delete + {:on-click #(do + (dom/stop-propagation %) + ;; (actions/delete-project conn uuid) + %)} + icons/trash]]])) + +(def project + (util/component + {:render project-render + :name "project" + :mixins [rum/static]})) ;; (defn sorted-projects ;; [projects sort-order] @@ -205,26 +214,41 @@ ;; project-cards ;; (reverse project-cards)))) -(rum/defc dashboard-grid < rum/reactive - [projects sort-order] - [:section.dashboard-grid - [:h2 "Your projects"] - [:div.dashboard-grid-content - (vec - (concat [:div.dashboard-grid-content - (new-project)] - #_(sorted-projects projects - (rum/react sort-order))))]]) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Grid +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn grid-render + [own] + (letfn [(on-click [e] + (dom/prevent-default e) + (lightbox/set! :new-project))] + (html + [:section.dashboard-grid + [:h2 "Your projects"] + [:div.dashboard-grid-content + [:div.dashboard-grid-content + [:div.grid-item.add-project {:on-click on-click} + [:span "+ New project"]]]]]))) + +(def grid + (util/component + {:render grid-render + :name "grid" + :mixins [rum/reactive]})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Main +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn dashboard-render [own] (html [:main.dashboard-main - (header) + (ui.h/header) [:section.dashboard-content - #_(dashboard-bar sort-order @project-count) - (dashboard-bar) - (dashboard-grid [] (atom :name))] + (menu) + (grid)] #_(timeline conn)])) diff --git a/frontend/uxbox/ui/dom.cljs b/frontend/uxbox/ui/dom.cljs new file mode 100644 index 0000000000..d23365fb4c --- /dev/null +++ b/frontend/uxbox/ui/dom.cljs @@ -0,0 +1,9 @@ +(ns uxbox.ui.dom) + +(defn stop-propagation + [e] + (.stopPropagation e)) + +(defn prevent-default + [e] + (.preventDefault e)) diff --git a/frontend/uxbox/ui/header.cljs b/frontend/uxbox/ui/header.cljs new file mode 100644 index 0000000000..f1f58fb02c --- /dev/null +++ b/frontend/uxbox/ui/header.cljs @@ -0,0 +1,25 @@ +(ns uxbox.ui.header + (:require [sablono.core :as html :refer-macros [html]] + [rum.core :as rum] + [uxbox.util :as util] + [uxbox.router :as r] + [uxbox.ui.navigation :as nav] + [uxbox.ui.icons :as i] + [uxbox.ui.users :as ui.u])) + +(defn header-render + [own] + (html + [:header#main-bar.main-bar + [:div.main-logo + (nav/link "/" i/logo)] + (ui.u/user)])) + +(def ^:static header + (util/component + {:render header-render + :name "header" + :mixins [rum/static]})) + + + diff --git a/frontend/uxbox/ui/keyboard.cljs b/frontend/uxbox/ui/keyboard.cljs new file mode 100644 index 0000000000..ec92dfb5a0 --- /dev/null +++ b/frontend/uxbox/ui/keyboard.cljs @@ -0,0 +1,39 @@ +(ns uxbox.ui.keyboard + (:require [goog.events :as events]) + (:import [goog.events EventType KeyCodes] + [goog.ui KeyboardShortcutHandler])) + + +(defn is-keycode? + [keycode] + (fn [e] + (= (.-keyCode e) keycode))) + +(def esc? (is-keycode? 27)) +(def enter? (is-keycode? 13)) + +;; (def workspace-event-keys +;; ["DELETE" "ESC" "CTRL+C" "CTRL+V" "CTRL+B" "CTRL+E" "CTRL+L" "SHIFT+Q" +;; "SHIFT+W" "SHIFT+E" "CTRL+SHIFT+I" "CTRL+SHIFT+F" "CTRL+SHIFT+C" +;; "CTRL+SHIFT+L" "CTRL+G" "CTRL+UP" "CTRL+DOWN" "CTRL+SHIFT+UP" +;; "CTRL+SHIFT+DOWN" "SHIFT+I" "SHIFT+0" "SHIFT+O"]) + +;; ;; Mixins + +;; (defn keyboard-keypress +;; "A mixin for capture keyboard events." +;; [event-keys] +;; (let [handler (KeyboardShortcutHandler. js/document)] +;; (doseq [shortcut event-keys] +;; (.registerShortcut handler shortcut shortcut)) + +;; {:will-mount (fn [state] +;; (events/listen handler +;; KeyboardShortcutHandler.EventType.SHORTCUT_TRIGGERED +;; ws/on-workspace-keypress) +;; state) +;; :will-unmount (fn [state] +;; (events/unlisten js/document +;; EventType.KEYDOWN +;; ws/on-workspace-keypress) +;; state)})) diff --git a/frontend/uxbox/ui/lightbox.cljs b/frontend/uxbox/ui/lightbox.cljs new file mode 100644 index 0000000000..702a171c8d --- /dev/null +++ b/frontend/uxbox/ui/lightbox.cljs @@ -0,0 +1,61 @@ +(ns uxbox.ui.lightbox + (:require [rum.core :as rum] + [uxbox.util :as util] + [uxbox.ui.keyboard :as k] + [goog.events :as events]) + (:import goog.events.EventType)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; State Management +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defonce +current+ (atom nil)) + +(defn set! + [kind] + (println "lightbox$set!" kind) + (reset! +current+ kind)) + +(defn close! + [] + (reset! +current+ nil)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; UI +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmulti render-lightbox identity) +(defmethod render-lightbox :default [_] nil) + +(defn- on-esc-clicked + [e] + (when (k/esc? e) + (close!))) + +(defn- lightbox-will-mount + [state] + (events/listen js/document + EventType.KEYDOWN + on-esc-clicked) + state) + +(defn- lightbox-will-umount + [state] + (events/unlisten js/document + EventType.KEYDOWN + on-esc-clicked) + state) + +(defn- lightbox-render + [own] + (let [name (rum/react +current+)] + [:div.lightbox {:class (when (nil? name) "hide")} + (render-lightbox name)])) + +(def lightbox + (util/component + {:name "lightbox" + :render lightbox-render + :will-mount lightbox-will-mount + :will-unmount lightbox-will-umount + :mixins [rum/reactive]}))