Created
January 28, 2016 08:37
-
-
Save selfsame/632290c0006d11922cde to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (ns sync.sheets | |
| (:require-macros [reagent.ratom :refer [reaction]]) | |
| (:require | |
| [om.next :as om :refer-macros [defui]] | |
| [om.dom :as dom] | |
| [heh.core :as heh :refer [private private!] :refer-macros [html]] | |
| [reagent.core :as r] | |
| [goog.events :as events] | |
| [css.units :as units] | |
| [css.core :as css] | |
| [dollar.bill :as $ :refer [$]] | |
| [pdfn.core :refer [and* or* not* is*] :refer-macros [defpdfn pdfn inspect]] | |
| [cljs.pprint :as pprint] | |
| [fast.style] | |
| [sync.mouse]) | |
| (:import | |
| [goog.events EventType])) | |
| (defn ratom? [v] (instance? reagent.ratom/RAtom v)) | |
| (def UID (atom 0)) | |
| (defn uid [] (swap! UID inc)) | |
| (defn preparse [v] | |
| (cond (integer? v) (str v "px") | |
| (keyword? v) (clj->js v) | |
| (number? v) (str v) | |
| (vector? v) (mapv preparse v) | |
| :else v)) | |
| (defn parse [v] | |
| (let [res | |
| (cond (string? v) (fast.style/parse-css-value v) | |
| (vector? v) (mapv parse v) | |
| :else [v])] | |
| (if (or (sequential? res)(array? res)) | |
| (case (count res) 0 nil 1 (first res) (vec res)) | |
| res))) | |
| (defn virtual-css [data] | |
| (zipmap (keys data) (map (comp parse preparse) (vals data)))) | |
| (defn render-css-block [rule m] | |
| (str "\n" rule " {\n" | |
| (apply str (mapcat (fn [[k v]] | |
| (interleave ["" (clj->js k) (str v)] [" " ":" ";\n"])) m)) "}")) | |
| ; DATA | |
| (def app-state {}) | |
| (def app-style (r/atom (virtual-css | |
| {:background "#D6C2AB" | |
| :text "black" | |
| :font-size "1em" | |
| :resizer-size "0.5em"}))) | |
| (def default-layout | |
| [1 [0.05 [0.5 :menu][0.5 :tabs]] | |
| [0.89 [0.15 | |
| [0.3 [0.5 :tool] [0.5 :mode]] | |
| [0.5 :style] | |
| [0.2 :selection]] | |
| [0.7 :workspace] | |
| [0.25 :outliner]] | |
| [0.06 [0.7 :context] | |
| [0.3 :meta]]]) | |
| (def window-dims (r/atom {:w 640 :h 480 :x 0 :y 0})) | |
| ;LAYOUT | |
| (defn register-layout | |
| ([data] (let [r (atom {})] (reset! UID -1) (register-layout data r) r)) | |
| ([data registry] | |
| (if-not (vector? data) data | |
| (let [id (uid)] | |
| (swap! registry conj | |
| {id {:id id | |
| :view (first (filter keyword? data)) | |
| :ratio (r/atom (first data)) | |
| :children (mapv #(register-layout % registry) (filter vector? data))}}) id)))) | |
| (def LAYOUT (register-layout default-layout)) | |
| (def axis-toggle {:h :v :v :h}) | |
| (def axis-dim {:h :h :v :w}) | |
| (def axis-axis {:h :y :v :x}) | |
| (defn measure-window [_] | |
| (reset! window-dims {:w (.-innerWidth js/window) :h (.-innerHeight js/window) :x 0 :y 0})) | |
| (defn derive-dims [me ratom axis prev] | |
| (let [dim-k (axis-dim axis) | |
| axis-k (axis-axis axis)] | |
| (reaction | |
| (assoc | |
| (if (:dims prev) (assoc @ratom axis-k (int (+ (get @(:dims prev) axis-k) (get @(:dims prev) dim-k) ))) | |
| @ratom) | |
| dim-k (int (* (or @(:ratio me) 1.0) (get @ratom dim-k))))))) | |
| (defn create-resizer [data -a -b] | |
| (let [[a b] (mapv @data [-a -b]) | |
| id (uid) | |
| size (:resizer-size @app-style)] | |
| (swap! data conj {id {:id id :resizer true | |
| :axis (:axis b) :neighbors (mapv :id [a b]) | |
| :dims (reaction | |
| (update | |
| (assoc @(:dims b) (axis-dim (:axis b)) size) | |
| (axis-axis (:axis b)) | |
| #(- % (* size 0.5)))) }}))) | |
| (defn ratom-walk | |
| ([data] (ratom-walk data 0 window-dims :v nil)) | |
| ([data id ratom axis prev] | |
| (let [me (get @data id) | |
| children (:children me)] | |
| (swap! data update-in [id] conj {:axis axis :dims (derive-dims me ratom axis prev)}) | |
| (vec (map-indexed | |
| (fn [idx child] | |
| (swap! data update-in [child] conj {:parent id}) | |
| (ratom-walk data child (:dims (get @data id)) (axis-toggle axis) (get @data (get children (dec idx))))) | |
| children)) | |
| (reduce (fn [a b] (if a (do (create-resizer data a b) b) b)) nil children)))) | |
| (defn legal-ratios [a b delta thresh] | |
| (let [-a (+ a delta) | |
| -b (- b delta) | |
| res | |
| (if (or | |
| (< -a thresh) | |
| (< (- 1 thresh) -a) | |
| (< -b thresh) | |
| (< (- 1 thresh) -b)) | |
| 0 delta)] | |
| [res (- res)])) | |
| (defn resize-fn [props delta] | |
| (prn "resize") | |
| (let [[a b] (map @LAYOUT (:neighbors props)) | |
| parent (@LAYOUT (:parent a)) | |
| delta (get delta ({:h 1 :v 0} (:axis props)) delta) | |
| ax (axis-axis (:axis props)) | |
| dim (axis-dim (:axis props)) | |
| p-dim (get @(:dims parent) dim) | |
| [r-a r-b] (legal-ratios @(:ratio a) @(:ratio b) (/ delta p-dim) (/ 24 p-dim))] | |
| (swap! (:ratio a) #(+ % r-a)) | |
| (swap! (:ratio b) #(+ % r-b)))) | |
| ; OM NEXT | |
| (defn get-normalized [state key] | |
| (let [st @state] | |
| (into [] (map #(get-in st %)) (get st key)))) | |
| (defn read-local | |
| [{:keys [query ast state] :as env} k params] | |
| (if (om/ident? (:key ast)) | |
| (get-in @state (:key ast)) | |
| (om/db->tree query (get @state k) @state))) | |
| (defpdfn ^:inline read) | |
| (pdfn read [env k params] {} | |
| {:value (get @(:state env) k)}) | |
| (pdfn read [env k params] | |
| {k (is* :interface)} | |
| {:value (filterv (or* :view :resizer) (vals @LAYOUT))}) | |
| (defpdfn ^:inline mutate) | |
| (pdfn mutate [env k props] {:action #()}) | |
| (def reconciler (om/reconciler { | |
| :logger nil | |
| :state app-state | |
| :parser (om/parser { | |
| :read read | |
| :mutate mutate})})) | |
| ; OM ui | |
| (defui Panel | |
| Object | |
| (render [this] | |
| (let [props (om/props this)] | |
| (html | |
| (<div.panel | |
| (class (str "pid" (:id props) " " ({:h "horizontal" :v "vertical"} (:axis props)))) | |
| (<div (style {:margin "0.2em 0.5em"}) | |
| (<code (str (:view props)) ))))))) | |
| (def panel (om/factory Panel)) | |
| (defui Resizer | |
| Object | |
| (render [this] | |
| (let [props (om/props this) | |
| axis-idx ({:h 0 :v 1} (:axis props))] | |
| (html | |
| (<div.resizer | |
| (class (str "pid" (:id props) " " ({:h "horizontal" :v "vertical"} (:axis props)) | |
| (if (:active (om/get-state this)) " active"))) | |
| (onMouseDown [e] | |
| (om/set-state! this {:active true}) | |
| (sync.mouse/drag-fns! { | |
| :drag #(resize-fn props (vec (:delta %))) | |
| :end #(om/set-state! this {:active false})})) ))))) | |
| (def resizer (om/factory Resizer)) | |
| (defui Main | |
| static om/IQueryParams | |
| (params [this] {}) | |
| static om/IQuery | |
| (query [this] | |
| `[:interface]) | |
| Object | |
| (render [this] | |
| (let [props (om/props this)] | |
| (pprint/pprint (filter :view (:interface props))) | |
| (html | |
| (<div | |
| (map panel (filter :view (:interface props))) | |
| (map resizer (filter :resizer (:interface props)))))))) | |
| ; REAGENT | |
| (defn panels [] | |
| [:style (str | |
| (render-css-block | |
| ".panel" | |
| {:position "absolute" | |
| :box-sizing "border-box" | |
| :background (:background @app-style)}) | |
| (render-css-block | |
| ".resizer" | |
| {:position "absolute" | |
| :height (:resizer-size @app-style) | |
| :box-sizing "border-box" | |
| :background "#8E785F" | |
| :cursor "n-resize"}) | |
| (render-css-block | |
| ".resizer.vertical" | |
| {:cursor "e-resize"}) | |
| (render-css-block | |
| ".resizer:hover, .resizer.active" | |
| {:background "#61D8CB"}))]) | |
| (defn panel-block [data] | |
| (let [{:keys [id dims]} data] | |
| [:style (render-css-block | |
| (str ".pid" id) | |
| {:top (str (int (:y @dims)) "px") | |
| :left (str (int (:x @dims)) "px") | |
| :height (str (int (:h @dims)) "px") | |
| :width (str (int (:w @dims)) "px")})])) | |
| (defn interface [] | |
| (vec (concat [:div [panels]] | |
| (mapv panel-block (filterv (or* :view :resizer) (vals @LAYOUT)))))) | |
| (defonce -virtual-el ($/append (first ($ "head")) ($ "<div id='virtual'></div>"))) | |
| (defn demo [] | |
| (events/listen js/window EventType.RESIZE measure-window) | |
| (measure-window nil) | |
| (ratom-walk LAYOUT) | |
| (r/render-component [interface] (first ($ "#virtual"))) | |
| (om/add-root! reconciler Main (first ($ "#main")))) | |
| ;TODO | |
| '[transform panel tree] | |
| '((x) cascade reactive dependencies and build reagent components) | |
| '((x) om/next read vector of leafs {:id 0 :view :foo}) | |
| '[interaction] | |
| '((x) resize handles) | |
| '(( ) add/remove parts of the tree - look at Blender and Unity) | |
| '[system] | |
| '(( ) ns for reagent style utils) | |
| '(( ) ns for subdividing ui fns) | |
| '(( ) ns for ratomic style) | |
| '(( ) ns for om/next read and mutate fns) | |
| '[conversion] | |
| '(( ) rewrite core for om/next) | |
| '(( ) load document and sync workspace with sub-ui) | |
| '(( ) convert app-state for selection and other basics needed by workspace) | |
| '(( ) rewrite css with ratoms) | |
| '(( ) integrate thi.ng/color) | |
| '(( ) base colors #{background, forground, active} reacted into UI variations) | |
| '(( ) base weights for deriving #{alpha contrast saturation layout-spacing}) | |
| '[components] | |
| '(( ) outliner) | |
| '(( ) style) | |
| '(( ) selection) | |
| '(( ) menu) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment