diff --git a/frontend/deps.edn b/frontend/deps.edn index b2c75f3aa..767150ced 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -1,24 +1,24 @@ {:deps - {org.clojure/clojure {:mvn/version "RELEASE"} - org.clojure/clojurescript {:mvn/version "RELEASE"} - - cljs-bean {:mvn/version "1.5.0"} - - uix.core {:git/url "https://github.com/roman01la/uix.git" - :deps/root "core" - :sha "46011c8a2f1a05025971af8e26b8b6704520383c"} - uix.dom {:git/url "https://github.com/roman01la/uix.git" - :deps/root "dom" - :sha "46011c8a2f1a05025971af8e26b8b6704520383c"} - + {;; dev thheller/shadow-cljs {:mvn/version "RELEASE"} cider/cider-nrepl {:mvn/version "0.23.0-SNAPSHOT"} binaryage/devtools {:mvn/version "0.9.10"} - rum {:mvn/version "0.11.4"} + + org.clojure/clojure {:mvn/version "RELEASE"} + org.clojure/clojurescript {:mvn/version "RELEASE"} + cljs-bean {:mvn/version "1.5.0"} + uix.core {:git/url "https://github.com/tiensonqin/uix.git" + :deps/root "core" + :sha "f6d82d12d62c13ff1894ac9dc2dd894a3521cd3d"} + uix.dom {:git/url "https://github.com/roman01la/uix.git" + :deps/root "dom" + :sha "46011c8a2f1a05025971af8e26b8b6704520383c"} datascript {:mvn/version "0.18.9"} funcool/promesa {:mvn/version "4.0.2"} medley {:mvn/version "1.2.0"} - } + metosin/reitit {:mvn/version "0.3.10"} + metosin/reitit-spec {:mvn/version "0.3.10"} + metosin/reitit-frontend {:mvn/version "0.3.10"}} :paths ["src" diff --git a/frontend/src/frontend/components/agenda.cljs b/frontend/src/frontend/components/agenda.cljs index 3d4063b16..1dfc5aab4 100644 --- a/frontend/src/frontend/components/agenda.cljs +++ b/frontend/src/frontend/components/agenda.cljs @@ -1,98 +1,99 @@ (ns frontend.components.agenda - (:require [rum.core :as rum] - [frontend.mui :as mui] - [frontend.util :as util] - [frontend.handler :as handler] - [frontend.format.org.block :as block] - [frontend.state :as state] - [clojure.string :as string] - [frontend.format.org-mode :as org])) - -(rum/defc timestamps-cp - [timestamps] - [:ul - (for [[type {:keys [date time]}] timestamps] - (let [{:keys [year month day]} date - {:keys [hour min]} time] - [:li {:key type} - [:span {:style {:margin-right 6}} type] - [:span (if time - (str year "-" month "-" day " " hour ":" min) - (str year "-" month "-" day))]]))]) - -(rum/defc title-cp - [title] - (let [title-json (js/JSON.stringify (clj->js title)) - html (org/inline-list->html title-json)] - (util/raw-html html))) - -(rum/defc marker-cp - [marker] - [:span {:class (str "marker-" (string/lower-case marker)) - :style {:margin-left 8}} - (if (contains? #{"DOING" "IN-PROGRESS"} marker) - (str " (" marker ")"))]) - -(rum/defc tags-cp - [tags] - [:span - (for [tag tags] - [:span.tag {:key tag} - [:span - tag]])]) - -(rum/defc agenda - [tasks] - [:span "TBD"] - ;; [:div#agenda - ;; (if (seq tasks) - ;; (for [[section-name tasks] tasks] - ;; [:div.section {:key (str "section-" section-name)} - ;; [:h3 section-name] - ;; (mui/list - ;; (for [[idx {:keys [marker title priority level tags children timestamps meta]}] (util/indexed (block/sort-tasks tasks))] - ;; (mui/list-item - ;; {:key (str "task-" section-name "-" idx) - ;; :style {:padding-left 8 - ;; :padding-right 8}} - ;; [:div.column - ;; [:div.row {:style {:align-items "center"}} - ;; (let [marker (case marker - ;; (list "DOING" "IN-PROGRESS" "TODO") - ;; (mui/checkbox {:checked false - ;; :on-change (fn [_] - ;; ;; FIXME: Log timestamp - ;; (handler/check marker (:pos meta))) - ;; :color "primary" - ;; :style {:padding 0}}) - - ;; "WAIT" - ;; [:span {:style {:font-weight "bold"}} - ;; "WAIT"] - - ;; "DONE" - ;; (mui/checkbox {:checked true - ;; :on-change (fn [_] - ;; ;; FIXME: rollback to the last state if exists. - ;; ;; it must not be `TODO` - ;; (handler/uncheck (:pos meta))) - ;; :color "primary" - ;; :style {:padding 0}}) - - ;; nil)] - ;; (if priority - ;; (mui/badge {:badge-content (string/lower-case priority) - ;; :overlay "circle"} - ;; marker) - ;; marker)) - - ;; [:div.row {:style {:margin-left 8}} - ;; (title-cp title) - ;; (marker-cp marker) - ;; (when (seq tags) - ;; (tags-cp tags))]] - ;; (when (seq timestamps) - ;; (timestamps-cp timestamps)) - ;; ])))]) - ;; "Empty")] + ;; (:require [rum.core :as rum] + ;; [frontend.mui :as mui] + ;; [frontend.util :as util] + ;; [frontend.handler :as handler] + ;; [frontend.format.org.block :as block] + ;; [frontend.state :as state] + ;; [clojure.string :as string] + ;; [frontend.format.org-mode :as org]) ) + +;; (rum/defc timestamps-cp +;; [timestamps] +;; [:ul +;; (for [[type {:keys [date time]}] timestamps] +;; (let [{:keys [year month day]} date +;; {:keys [hour min]} time] +;; [:li {:key type} +;; [:span {:style {:margin-right 6}} type] +;; [:span (if time +;; (str year "-" month "-" day " " hour ":" min) +;; (str year "-" month "-" day))]]))]) + +;; (rum/defc title-cp +;; [title] +;; (let [title-json (js/JSON.stringify (clj->js title)) +;; html (org/inline-list->html title-json)] +;; (util/raw-html html))) + +;; (rum/defc marker-cp +;; [marker] +;; [:span {:class (str "marker-" (string/lower-case marker)) +;; :style {:margin-left 8}} +;; (if (contains? #{"DOING" "IN-PROGRESS"} marker) +;; (str " (" marker ")"))]) + +;; (rum/defc tags-cp +;; [tags] +;; [:span +;; (for [tag tags] +;; [:span.tag {:key tag} +;; [:span +;; tag]])]) + +;; (rum/defc agenda +;; [tasks] +;; [:span "TBD"] +;; ;; [:div#agenda +;; ;; (if (seq tasks) +;; ;; (for [[section-name tasks] tasks] +;; ;; [:div.section {:key (str "section-" section-name)} +;; ;; [:h3 section-name] +;; ;; (mui/list +;; ;; (for [[idx {:keys [marker title priority level tags children timestamps meta]}] (util/indexed (block/sort-tasks tasks))] +;; ;; (mui/list-item +;; ;; {:key (str "task-" section-name "-" idx) +;; ;; :style {:padding-left 8 +;; ;; :padding-right 8}} +;; ;; [:div.column +;; ;; [:div.row {:style {:align-items "center"}} +;; ;; (let [marker (case marker +;; ;; (list "DOING" "IN-PROGRESS" "TODO") +;; ;; (mui/checkbox {:checked false +;; ;; :on-change (fn [_] +;; ;; ;; FIXME: Log timestamp +;; ;; (handler/check marker (:pos meta))) +;; ;; :color "primary" +;; ;; :style {:padding 0}}) + +;; ;; "WAIT" +;; ;; [:span {:style {:font-weight "bold"}} +;; ;; "WAIT"] + +;; ;; "DONE" +;; ;; (mui/checkbox {:checked true +;; ;; :on-change (fn [_] +;; ;; ;; FIXME: rollback to the last state if exists. +;; ;; ;; it must not be `TODO` +;; ;; (handler/uncheck (:pos meta))) +;; ;; :color "primary" +;; ;; :style {:padding 0}}) + +;; ;; nil)] +;; ;; (if priority +;; ;; (mui/badge {:badge-content (string/lower-case priority) +;; ;; :overlay "circle"} +;; ;; marker) +;; ;; marker)) + +;; ;; [:div.row {:style {:margin-left 8}} +;; ;; (title-cp title) +;; ;; (marker-cp marker) +;; ;; (when (seq tags) +;; ;; (tags-cp tags))]] +;; ;; (when (seq timestamps) +;; ;; (timestamps-cp timestamps)) +;; ;; ])))]) +;; ;; "Empty")] +;; ) diff --git a/frontend/src/frontend/components/file.cljs b/frontend/src/frontend/components/file.cljs index a73d0d93c..ebbe75a9e 100644 --- a/frontend/src/frontend/components/file.cljs +++ b/frontend/src/frontend/components/file.cljs @@ -1,66 +1,67 @@ (ns frontend.components.file - (:require [rum.core :as rum] - [frontend.mui :as mui] - ["@material-ui/core/colors" :as colors] - [frontend.state :as state] - [frontend.util :as util] - [frontend.handler :as handler] - [clojure.string :as string])) + ;; (:require [rum.core :as rum] + ;; [frontend.mui :as mui] + ;; ["@material-ui/core/colors" :as colors] + ;; [frontend.state :as state] + ;; [frontend.util :as util] + ;; [frontend.handler :as handler] + ;; [clojure.string :as string]) + ) -(rum/defc files-list - [current-repo files] - [:div - (if (seq files) - (let [files-set (set files) - prefix [(files-set "tasks.org")] - files (->> (remove (set prefix) files) - (concat prefix) - (remove nil?))] - (mui/list - (for [file files] - (mui/list-item - {:button true - :key file - :style {:overflow "hidden"} - :on-click (fn [] - (handler/load-file current-repo file) - (handler/toggle-drawer? false))} - (mui/list-item-text file))))) - "Loading...")]) +;; (rum/defc files-list +;; [current-repo files] +;; [:div +;; (if (seq files) +;; (let [files-set (set files) +;; prefix [(files-set "tasks.org")] +;; files (->> (remove (set prefix) files) +;; (concat prefix) +;; (remove nil?))] +;; (mui/list +;; (for [file files] +;; (mui/list-item +;; {:button true +;; :key file +;; :style {:overflow "hidden"} +;; :on-click (fn [] +;; (handler/load-file current-repo file) +;; (handler/toggle-drawer? false))} +;; (mui/list-item-text file))))) +;; "Loading...")]) -(rum/defc edit < rum/reactive - [] - (let [state (rum/react state/state) - {:keys [current-repo current-file contents]} state] - (mui/container - {:id "root-container" - :style {:display "flex" - :justify-content "center" - :margin-top 64}} - [:div.column - (let [paths [:editing-files current-file]] - (mui/textarea {:style {:margin-bottom 12 - :padding 8 - :min-height 300} - :auto-focus true - :on-change (fn [event] - (let [v (util/evalue event)] - (swap! state/state assoc-in paths v))) - :default-value (get contents current-file) - :value (get-in state/state paths)})) - (let [path [:commit-message current-file]] - (mui/text-field {:id "standard-basic" - :style {:margin-bottom 12} - :label "Commit message" - :auto-focus true - :on-change (fn [event] - (let [v (util/evalue event)] - (when-not (string/blank? v) - (swap! state/state assoc-in path v)))) - :default-value (str "Update " current-file) - :value (get-in state/state path)})) - (mui/button {:variant "contained" - :color "primary" - :on-click (fn [] - (handler/alter-file current-repo current-file))} - "Submit")]))) +;; (rum/defc edit < rum/reactive +;; [] +;; (let [state (rum/react state/state) +;; {:keys [current-repo current-file contents]} state] +;; (mui/container +;; {:id "root-container" +;; :style {:display "flex" +;; :justify-content "center" +;; :margin-top 64}} +;; [:div.column +;; (let [paths [:editing-files current-file]] +;; (mui/textarea {:style {:margin-bottom 12 +;; :padding 8 +;; :min-height 300} +;; :auto-focus true +;; :on-change (fn [event] +;; (let [v (util/evalue event)] +;; (swap! state/state assoc-in paths v))) +;; :default-value (get contents current-file) +;; :value (get-in state/state paths)})) +;; (let [path [:commit-message current-file]] +;; (mui/text-field {:id "standard-basic" +;; :style {:margin-bottom 12} +;; :label "Commit message" +;; :auto-focus true +;; :on-change (fn [event] +;; (let [v (util/evalue event)] +;; (when-not (string/blank? v) +;; (swap! state/state assoc-in path v)))) +;; :default-value (str "Update " current-file) +;; :value (get-in state/state path)})) +;; (mui/button {:variant "contained" +;; :color "primary" +;; :on-click (fn [] +;; (handler/alter-file current-repo current-file))} +;; "Submit")]))) diff --git a/frontend/src/frontend/components/home.cljs b/frontend/src/frontend/components/home.cljs index eaf08f87e..85706c771 100644 --- a/frontend/src/frontend/components/home.cljs +++ b/frontend/src/frontend/components/home.cljs @@ -1,94 +1,220 @@ (ns frontend.components.home - (:require [rum.core :as rum] - [frontend.mui :as mui] - ["@material-ui/core/colors" :as colors] - [frontend.state :as state] + (:require [frontend.state :as state] [frontend.util :as util] [frontend.handler :as handler] - [frontend.components.agenda :as agenda] - [frontend.components.file :as file] - [frontend.components.settings :as settings] - [frontend.components.repo :as repo] + [frontend.ui :as ui] + [frontend.hooks :as hooks] + [uix.core.alpha :as uix] + ;; [frontend.components.agenda :as agenda] + ;; [frontend.components.file :as file] + ;; [frontend.components.settings :as settings] + ;; [frontend.components.repo :as repo] [frontend.format :as format] - [clojure.string :as string])) + [clojure.string :as string]) + ) -(rum/defc content-html - < {:did-mount (fn [state] - (doseq [block (-> (js/document.querySelectorAll "pre code") - (array-seq))] - (js/hljs.highlightBlock block)) - state)} - [current-file html-content] - [:div - (mui/link {:style {:float "right"} - :on-click (fn [] - (handler/change-page :edit-file))} - "edit") - (util/raw-html html-content)]) - -(rum/defc home < rum/reactive +(defn home [] - (let [state (rum/react state/state) - {:keys [user tokens repos repo-url github-token github-repo contents loadings current-repo current-file width drawer? tasks cloning?]} state - current-repo (or current-repo - (when-let [first-repo (first (keys repos))] - (handler/set-current-repo first-repo) - first-repo)) - files (get-in state [:repos current-repo :files]) - cloned? (get-in state [:repos current-repo :cloned?]) - loading? (get loadings current-file) - width (or width (util/get-width)) - mobile? (and width (<= width 600))] - (prn {:current-repo current-repo - :cloned? cloned?}) - (mui/container - {:id "root-container" - :style {:display "flex" - :justify-content "center" - ;; TODO: fewer spacing for mobile, 24px - :margin-top 64}} - (cond - (nil? user) - (mui/button {:variant "contained" - :color "primary" - :start-icon (mui/github-icon) - :href "/login/github"} - "Login with Github") + (let [ref (uix/ref nil) + open? (uix/state false) + close-fn (fn [] (reset! open? false)) + open-fn (fn [] (reset! open? true))] + (prn "open: " open?) + ;; effects + (hooks/setup-close-listener! ref open?) + [:div.relative.bg-white.overflow-hidden {:ref ref} + [:div.max-w-screen-xl.mx-auto + [:div.relative.z-10.pb-8.bg-white.sm:pb-16.md:pb-20.lg:max-w-2xl.lg:w-full.lg:pb-28.xl:pb-32 + [:div.pt-6.px-4.sm:px-6.lg:px-8 + [:nav.relative.flex.items-center.justify-between.sm:h-10.lg:justify-start + [:div.flex.items-center.flex-grow.flex-shrink-0.lg:flex-grow-0 + [:div.flex.items-center.justify-between.w-full.md:w-auto + [:a + {:href "#"} + [:img.h-8.w-auto.sm:h-10 + {:alt "", :src "https://tailwindui.com/img/logos/workflow-mark-on-white.svg"}]] + [:div.-mr-2.flex.items-center.md:hidden + [:button.inline-flex.items-center.justify-center.p-2.rounded-md.text-gray-400.hover:text-gray-500.hover:bg-gray-100.focus:outline-none.focus:bg-gray-100.focus:text-gray-500.transition.duration-150.ease-in-out + {:type "button", + :on-click open-fn} + [:svg.h-6.w-6 + {:viewbox "0 0 24 24", + :fill "none", + :stroke "currentColor"} + [:path + {:d "M4 6h16M4 12h16M4 18h16", + :stroke-width "2", + :stroke-linejoin "round", + :stroke-linecap "round"}]]]]]] + [:div.hidden.md:block.md:ml-10.md:pr-4 + [:a.font-medium.text-gray-500.hover:text-gray-900.focus:outline-none.focus:text-gray-900.transition.duration-150.ease-in-out + {:href "#"} + "Product"] + [:a.ml-8.font-medium.text-gray-500.hover:text-gray-900.focus:outline-none.focus:text-gray-900.transition.duration-150.ease-in-out + {:href "#"} + "Features"] + [:a.ml-8.font-medium.text-gray-500.hover:text-gray-900.focus:outline-none.focus:text-gray-900.transition.duration-150.ease-in-out + {:href "#"} + "Marketplace"] + [:a.ml-8.font-medium.text-gray-500.hover:text-gray-900.focus:outline-none.focus:text-gray-900.transition.duration-150.ease-in-out + {:href "#"} + "Company"] + [:a.ml-8.font-medium.text-indigo-600.hover:text-indigo-900.focus:outline-none.focus:text-indigo-700.transition.duration-150.ease-in-out + {:href "#"} + "Log in"]]]] + (ui/css-transition @open? 0 + (fn [state] + [:div.absolute.top-0.inset-x-0.p-2.transition.transform.origin-top-right.md:hidden + {:class (case state + "entering" "duration-150 ease-out opacity-0 scale-95" + "entered" "duration-150 ease-out opacity-100 scale-100" + "exiting" "duration-100 ease-in opacity-100 scale-100" + "exited" "duration-100 ease-in opacity-0 scale-95")} + [:div.rounded-lg.shadow-md + [:div.rounded-lg.bg-white.shadow-xs.overflow-hidden + [:div.px-5.pt-4.flex.items-center.justify-between + [:div + [:img.h-8.w-auto + {:alt "", :src "https://tailwindui.com/img/logos/workflow-mark-on-white.svg"}]] + [:div.-mr-2 + [:button.inline-flex.items-center.justify-center.p-2.rounded-md.text-gray-400.hover:text-gray-500.hover:bg-gray-100.focus:outline-none.focus:bg-gray-100.focus:text-gray-500.transition.duration-150.ease-in-out + {:type "button", + :on-click close-fn} + [:svg.h-6.w-6 + {:viewbox "0 0 24 24", + :fill "none", + :stroke "currentColor"} + [:path + {:d "M6 18L18 6M6 6l12 12", + :stroke-width "2", + :stroke-linejoin "round", + :stroke-linecap "round"}]]]]] + [:div.px-2.pt-2.pb-3 + [:a.block.px-3.py-2.rounded-md.text-base.font-medium.text-gray-700.hover:text-gray-900.hover:bg-gray-50.focus:outline-none.focus:text-gray-900.focus:bg-gray-50.transition.duration-150.ease-in-out + {:href "#"} + "Product"] + [:a.mt-1.block.px-3.py-2.rounded-md.text-base.font-medium.text-gray-700.hover:text-gray-900.hover:bg-gray-50.focus:outline-none.focus:text-gray-900.focus:bg-gray-50.transition.duration-150.ease-in-out + {:href "#"} + "Features"] + [:a.mt-1.block.px-3.py-2.rounded-md.text-base.font-medium.text-gray-700.hover:text-gray-900.hover:bg-gray-50.focus:outline-none.focus:text-gray-900.focus:bg-gray-50.transition.duration-150.ease-in-out + {:href "#"} + "Marketplace"] + [:a.mt-1.block.px-3.py-2.rounded-md.text-base.font-medium.text-gray-700.hover:text-gray-900.hover:bg-gray-50.focus:outline-none.focus:text-gray-900.focus:bg-gray-50.transition.duration-150.ease-in-out + {:href "#"} + "Company"]] + [:div + [:a.block.w-full.px-5.py-3.text-center.font-medium.text-indigo-600.bg-gray-50.hover:bg-gray-100.hover:text-indigo-700.focus:outline-none.focus:bg-gray-100.focus:text-indigo-700.transition.duration-150.ease-in-out + {:href "#"} + "\n Log in\n "]]]]])) - (empty? repos) - (repo/add-repo repo-url) + [:div.mt-10.mx-auto.max-w-screen-xl.px-4.sm:mt-12.sm:px-6.md:mt-16.lg:mt-20.lg:px-8.xl:mt-28 + [:div.sm:text-center.lg:text-left + [:h2.text-4xl.tracking-tight.leading-10.font-extrabold.text-gray-900.sm:text-5xl.sm:leading-none.md:text-6xl + "\n Data to enrich your\n " + [:br.xl:hidden] + [:span.text-indigo-600 "online business"]] + [:p.mt-3.text-base.text-gray-500.sm:mt-5.sm:text-lg.sm:max-w-xl.sm:mx-auto.md:mt-5.md:text-xl.lg:mx-0 + "\n Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem cupidatat commodo. Elit sunt amet fugiat veniam occaecat fugiat aliqua.\n "] + [:div.mt-5.sm:mt-8.sm:flex.sm:justify-center.lg:justify-start + [:div.rounded-md.shadow + [:a.w-full.flex.items-center.justify-center.px-8.py-3.border.border-transparent.text-base.leading-6.font-medium.rounded-md.text-white.bg-indigo-600.hover:bg-indigo-500.focus:outline-none.focus:shadow-outline.transition.duration-150.ease-in-out.md:py-4.md:text-lg.md:px-10 + {:href "#"} + "Login with Github"]] + [:div.mt-3.sm:mt-0.sm:ml-3 + [:a.w-full.flex.items-center.justify-center.px-8.py-3.border.border-transparent.text-base.leading-6.font-medium.rounded-md.text-indigo-700.bg-indigo-100.hover:text-indigo-600.hover:bg-indigo-50.focus:outline-none.focus:shadow-outline.focus:border-indigo-300.transition.duration-150.ease-in-out.md:py-4.md:text-lg.md:px-10 + {:href "#"} + "Live demo"]]]]] + [:svg + {:class (util/hiccup->class ".hidden.lg:block.absolute.right-0.inset-y-0.h-full.w-48.text-white.transform.translate-x-1/2") + :preserveaspectratio "none", + :viewbox "0 0 100 100", + :fill "currentColor"} + [:polygon {:points "50,0 100,0 50,100 0,100"}]]]] + [:div + {:class (util/hiccup->class ".lg:absolute.lg:inset-y-0.lg:right-0.lg:w-1/2")} + [:img.h-56.w-full.object-cover.sm:h-72.md:h-96.lg:w-full.lg:h-full + {:alt "", + :src + "https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2850&q=80"}]]]) + ) - cloned? - (mui/grid - {:container true - :spacing 3} - (when-not mobile? - (mui/grid {:xs 2} - (file/files-list current-repo files))) +;; (rum/defc content-html +;; < {:did-mount (fn [state] +;; (doseq [block (-> (js/document.querySelectorAll "pre code") +;; (array-seq))] +;; (js/hljs.highlightBlock block)) +;; state)} +;; [current-file html-content] +;; [:div +;; (mui/link {:style {:float "right"} +;; :on-click (fn [] +;; (handler/change-page :edit-file))} +;; "edit") +;; (util/raw-html html-content)]) - (if (and (not mobile?) - (not drawer?)) - (mui/divider {:orientation "vertical" - :style {:margin "0 24px"}})) - (mui/grid {:xs 9 - :style {:margin-left (if (or mobile? drawer?) 24 0)}} - (cond - (nil? current-file) - (agenda/agenda tasks) +;; (rum/defc home < rum/reactive +;; [] +;; (let [state (rum/react state/state) +;; {:keys [user tokens repos repo-url github-token github-repo contents loadings current-repo current-file width drawer? tasks cloning?]} state +;; current-repo (or current-repo +;; (when-let [first-repo (first (keys repos))] +;; (handler/set-current-repo first-repo) +;; first-repo)) +;; files (get-in state [:repos current-repo :files]) +;; cloned? (get-in state [:repos current-repo :cloned?]) +;; loading? (get loadings current-file) +;; width (or width (util/get-width)) +;; mobile? (and width (<= width 600))] +;; (prn {:current-repo current-repo +;; :cloned? cloned?}) +;; (mui/container +;; {:id "root-container" +;; :style {:display "flex" +;; :justify-content "center" +;; ;; TODO: fewer spacing for mobile, 24px +;; :margin-top 64}} +;; (cond +;; (nil? user) +;; (mui/button {:variant "contained" +;; :color "primary" +;; :start-icon (mui/github-icon) +;; :href "/login/github"} +;; "Login with Github") - loading? - [:div "Loading ..."] +;; (empty? repos) +;; (repo/add-repo repo-url) - :else - (let [content (get contents current-file) - suffix (last (string/split current-file #"\."))] - (if (and suffix (contains? #{"md" "markdown" "org"} suffix)) - (content-html current-file (format/to-html content suffix)) - [:div "File " suffix " is not supported."]))))) - cloning? - [:div "Cloning..."] +;; cloned? +;; (mui/grid +;; {:container true +;; :spacing 3} +;; (when-not mobile? +;; (mui/grid {:xs 2} +;; (file/files-list current-repo files))) - :else - [:div "TBC"] - ;; (settings/settings-form github-token github-repo) - )))) +;; (if (and (not mobile?) +;; (not drawer?)) +;; (mui/divider {:orientation "vertical" +;; :style {:margin "0 24px"}})) +;; (mui/grid {:xs 9 +;; :style {:margin-left (if (or mobile? drawer?) 24 0)}} +;; (cond +;; (nil? current-file) +;; (agenda/agenda tasks) + +;; loading? +;; [:div "Loading ..."] + +;; :else +;; (let [content (get contents current-file) +;; suffix (last (string/split current-file #"\."))] +;; (if (and suffix (contains? #{"md" "markdown" "org"} suffix)) +;; (content-html current-file (format/to-html content suffix)) +;; [:div "File " suffix " is not supported."]))))) +;; cloning? +;; [:div "Cloning..."] + +;; :else +;; [:div "TBC"] +;; ;; (settings/settings-form github-token github-repo) +;; )))) diff --git a/frontend/src/frontend/components/repo.cljs b/frontend/src/frontend/components/repo.cljs index a8df63f80..d65fa6f5a 100644 --- a/frontend/src/frontend/components/repo.cljs +++ b/frontend/src/frontend/components/repo.cljs @@ -1,38 +1,39 @@ (ns frontend.components.repo - (:require [rum.core :as rum] - [frontend.mui :as mui] - [frontend.util :as util] - [frontend.state :as state] - [frontend.handler :as handler] - [clojure.string :as string])) + ;; (:require [rum.core :as rum] + ;; [frontend.mui :as mui] + ;; [frontend.util :as util] + ;; [frontend.state :as state] + ;; [frontend.handler :as handler] + ;; [clojure.string :as string]) + ) -(defn repos - [repos] - (when (seq repos) - [:div#repos - [:ul - (for [{:keys [url id]} (vals repos)] - [:li {:key id} - [:button {:on-click (fn [] - (handler/set-current-repo url))} - (string/replace url "https://github.com/" "")]])]])) +;; (defn repos +;; [repos] +;; (when (seq repos) +;; [:div#repos +;; [:ul +;; (for [{:keys [url id]} (vals repos)] +;; [:li {:key id} +;; [:button {:on-click (fn [] +;; (handler/set-current-repo url))} +;; (string/replace url "https://github.com/" "")]])]])) -(defn add-repo - [repo-url] - [:form {:style {:min-width 300}} - (mui/grid - {:container true - :direction "column"} - (mui/text-field {:id "standard-basic" - :style {:margin-bottom 12} - :label "Repo url" - :on-change (fn [event] - (let [v (util/evalue event)] - (swap! state/state assoc :repo-url v))) - :value repo-url - }) - (mui/button {:variant "contained" - :color "primary" - :on-click (fn [] - (handler/add-repo-and-clone repo-url))} - "Sync"))]) +;; (defn add-repo +;; [repo-url] +;; [:form {:style {:min-width 300}} +;; (mui/grid +;; {:container true +;; :direction "column"} +;; (mui/text-field {:id "standard-basic" +;; :style {:margin-bottom 12} +;; :label "Repo url" +;; :on-change (fn [event] +;; (let [v (util/evalue event)] +;; (swap! state/state assoc :repo-url v))) +;; :value repo-url +;; }) +;; (mui/button {:variant "contained" +;; :color "primary" +;; :on-click (fn [] +;; (handler/add-repo-and-clone repo-url))} +;; "Sync"))]) diff --git a/frontend/src/frontend/components/settings.cljs b/frontend/src/frontend/components/settings.cljs index 11995d6ee..dd2976e57 100644 --- a/frontend/src/frontend/components/settings.cljs +++ b/frontend/src/frontend/components/settings.cljs @@ -1,50 +1,51 @@ (ns frontend.components.settings - (:require [rum.core :as rum] - [frontend.mui :as mui] - [frontend.util :as util] - [frontend.state :as state] - [frontend.handler :as handler] - [clojure.string :as string])) + ;; (:require [rum.core :as rum] + ;; [frontend.mui :as mui] + ;; [frontend.util :as util] + ;; [frontend.state :as state] + ;; [frontend.handler :as handler] + ;; [clojure.string :as string]) + ) -(defn settings-form - [github-token github-repo] - [:form {:style {:min-width 300}} - (mui/grid - {:container true - :direction "column"} - (mui/text-field {:id "standard-basic" - :style {:margin-bottom 12} - :label "Github repo" - :on-change (fn [event] - (let [v (util/evalue event)] - (swap! state/state assoc :github-repo v))) - :value github-repo - }) - (mui/button {:variant "contained" - :color "primary" - :on-click (fn [] - (when (and github-token github-repo) - (handler/clone github-token github-repo)))} - "Sync"))]) +;; (defn settings-form +;; [github-token github-repo] +;; [:form {:style {:min-width 300}} +;; (mui/grid +;; {:container true +;; :direction "column"} +;; (mui/text-field {:id "standard-basic" +;; :style {:margin-bottom 12} +;; :label "Github repo" +;; :on-change (fn [event] +;; (let [v (util/evalue event)] +;; (swap! state/state assoc :github-repo v))) +;; :value github-repo +;; }) +;; (mui/button {:variant "contained" +;; :color "primary" +;; :on-click (fn [] +;; (when (and github-token github-repo) +;; (handler/clone github-token github-repo)))} +;; "Sync"))]) -(rum/defc settings < rum/reactive - [] - ;; Change repo and basic token - (let [state (rum/react state/state) - {:keys [github-token github-repo]} state] - (mui/container - {:id "root-container" - :style {:display "flex" - :justify-content "center" - :margin-top 64}} +;; (rum/defc settings < rum/reactive +;; [] +;; ;; Change repo and basic token +;; (let [state (rum/react state/state) +;; {:keys [github-token github-repo]} state] +;; (mui/container +;; {:id "root-container" +;; :style {:display "flex" +;; :justify-content "center" +;; :margin-top 64}} - [:div +;; [:div - (settings-form github-token github-repo) +;; (settings-form github-token github-repo) - (mui/divider {:style {:margin "24px 0"}}) +;; (mui/divider {:style {:margin "24px 0"}}) - ;; clear storage - (mui/button {:on-click handler/clear-storage - :color "primary"} - "Clear storage and clone")]))) +;; ;; clear storage +;; (mui/button {:on-click handler/clear-storage +;; :color "primary"} +;; "Clear storage and clone")]))) diff --git a/frontend/src/frontend/components/sidebar.cljs b/frontend/src/frontend/components/sidebar.cljs index cd94728ff..8de99554b 100644 --- a/frontend/src/frontend/components/sidebar.cljs +++ b/frontend/src/frontend/components/sidebar.cljs @@ -1,18 +1,47 @@ (ns frontend.components.sidebar - (:require [rum.core :as rum] + (:require [uix.core.alpha :as uix :refer [defui]] + [xframe.core.alpha :as xf :refer [ state ::event-handler .removeAll)) + +(defn listen + "Register an event `handler` for events of `type` on `target`." + [^EventHandler event-handler target type handler & [opts]] + (.listen event-handler target (name type) handler (clj->js opts))) + +;; (defn timeout-mixin +;; "The setTimeout mixin." +;; [name t f] +;; {:will-mount +;; (fn [state] +;; (assoc state name (util/set-timeout t f))) +;; :will-unmount +;; (fn [state] +;; (let [timeout (get state name)] +;; (util/clear-timeout timeout) +;; (dissoc state name)))}) + +;; (defn interval-mixin +;; "The setInterval mixin." +;; [name t f] +;; {:will-mount +;; (fn [state] +;; (assoc state name (util/set-interval t f))) +;; :will-unmount +;; (fn [state] +;; (when-let [interval (get state name)] +;; (util/clear-interval interval)) +;; (dissoc state name))}) + +(defn event-hook + [attach-listeners] + (let [event-handler (uix/state (EventHandler.))] + (uix/effect! + (fn [] + ;; did mount + (attach-listeners @event-handler) + (fn [] + ;; will-unmount + (detach-listeners @event-handler))) + []))) + +(defn close-when-esc-or-outside + [ref event-handler open? & {:keys [on-close]}] + (let [node (uix-dom/find-dom-node @ref)] + (when open? + (when node + (listen event-handler js/window "click" + (fn [e] + ;; If the click target is outside of current node + (when-not (dom/contains node (.. e -target)) + (on-close e))))) + + (listen event-handler js/window "keydown" + (fn [e] + (case (.-keyCode e) + ;; Esc + 27 (on-close e) + nil)))))) + +(defn setup-close-listener! + [ref open?] + (event-hook (fn [event-handler] + (close-when-esc-or-outside + ref + event-handler + open? + :on-close (fn [] + (reset! open? false)))))) diff --git a/frontend/src/frontend/layout.cljs b/frontend/src/frontend/layout.cljs index 4cb360160..2f977639b 100644 --- a/frontend/src/frontend/layout.cljs +++ b/frontend/src/frontend/layout.cljs @@ -1,66 +1,66 @@ (ns frontend.layout - (:require [frontend.mui :as mui] - [frontend.handler :as handler] - [frontend.state :as state] - [frontend.components.file :as file] - [frontend.components.repo :as repo] - [rum.core :as rum] - [clojure.string :as string])) + ;; (:require [frontend.handler :as handler] + ;; [frontend.state :as state] + ;; [frontend.components.file :as file] + ;; [frontend.components.repo :as repo] + ;; [rum.core :as rum] + ;; [clojure.string :as string]) + ) -(rum/defc frame < rum/reactive - [content width] - (let [state (rum/react state/state) - {:keys [files drawer? snackbar? snackbar-message current-repo]} state - mobile? (and width (<= width 600))] - (mui/theme-provider - {:theme (mui/custom-theme)} - [:div {:class "root" - :style {:padding-bottom 100}} - (mui/css-baseline) - (mui/app-bar - {:position "static"} - (mui/tool-bar - {} - (if mobile? - (mui/icon-button {:edge "start" - :class "menuButton" - :color "inherit" - :on-click (fn [] - (handler/toggle-drawer? true))} - (mui/menu-icon))) - (mui/typography {:class "grow" - :variant "h6" - :color "inherit" - :no-wrap true - :on-click (fn [] - (handler/change-page :home) - (handler/reset-current-file))} - "Gitnotes") +;; (rum/defc frame < rum/reactive +;; [content width] +;; (let [state (rum/react state/state) +;; {:keys [files drawer? snackbar? snackbar-message current-repo]} state +;; mobile? (and width (<= width 600))] +;; (mui/theme-provider +;; {:theme (mui/custom-theme)} +;; [:div {:class "root" +;; :style {:padding-bottom 100}} +;; (mui/css-baseline) +;; (mui/app-bar +;; {:position "static"} +;; (mui/tool-bar +;; {} +;; (if mobile? +;; (mui/icon-button {:edge "start" +;; :class "menuButton" +;; :color "inherit" +;; :on-click (fn [] +;; (handler/toggle-drawer? true))} +;; (mui/menu-icon))) +;; (mui/typography {:class "grow" +;; :variant "h6" +;; :color "inherit" +;; :no-wrap true +;; :on-click (fn [] +;; (handler/change-page :home) +;; (handler/reset-current-file))} +;; "Gitnotes") - (mui/button {:color "inherit" - :on-click (fn [] - (handler/sync))} - "Sync") +;; (mui/button {:color "inherit" +;; :on-click (fn [] +;; (handler/sync))} +;; "Sync") - (mui/button {:color "inherit" - :on-click (fn [] - (handler/change-page :settings))} - "Settings"))) +;; (mui/button {:color "inherit" +;; :on-click (fn [] +;; (handler/change-page :settings))} +;; "Settings"))) - (repo/repos (:repos state)) +;; (repo/repos (:repos state)) - content +;; content - (if mobile? - (mui/drawer {:open drawer? - :disableBackdropTransition true - :on-open (fn [] - (handler/toggle-drawer? true)) - :on-close (fn [] - (handler/toggle-drawer? false))} - [:div {:style {:width 240}} - (file/files-list current-repo files)])) +;; (if mobile? +;; (mui/drawer {:open drawer? +;; :disableBackdropTransition true +;; :on-open (fn [] +;; (handler/toggle-drawer? true)) +;; :on-close (fn [] +;; (handler/toggle-drawer? false))} +;; [:div {:style {:width 240}} +;; (file/files-list current-repo files)])) - (mui/snackbar {:open snackbar? - :auto-hide-duration 3000 - :message snackbar-message})]))) +;; (mui/snackbar {:open snackbar? +;; :auto-hide-duration 3000 +;; :message snackbar-message})]))) diff --git a/frontend/src/frontend/mixins.cljs b/frontend/src/frontend/mixins.cljs deleted file mode 100644 index 8a0c808e4..000000000 --- a/frontend/src/frontend/mixins.cljs +++ /dev/null @@ -1,86 +0,0 @@ -(ns frontend.mixins - (:require [rum.core :as rum] - [goog.dom :as dom]) - (:import [goog.events EventHandler])) - -(defn detach - "Detach all event listeners." - [state] - (some-> state ::event-handler .removeAll)) - -(defn listen - "Register an event `handler` for events of `type` on `target`." - [state target type handler & [opts]] - (when-let [event-handler (::event-handler state)] - (.listen event-handler target (name type) handler (clj->js opts)))) - -(def event-handler-mixin - "The event handler mixin." - {:will-mount - (fn [state] - (assoc state ::event-handler (EventHandler.))) - :will-unmount - (fn [state] - (detach state) - (dissoc state ::event-handler))}) - -;; (defn timeout-mixin -;; "The setTimeout mixin." -;; [name t f] -;; {:will-mount -;; (fn [state] -;; (assoc state name (util/set-timeout t f))) -;; :will-unmount -;; (fn [state] -;; (let [timeout (get state name)] -;; (util/clear-timeout timeout) -;; (dissoc state name)))}) - -;; (defn interval-mixin -;; "The setInterval mixin." -;; [name t f] -;; {:will-mount -;; (fn [state] -;; (assoc state name (util/set-interval t f))) -;; :will-unmount -;; (fn [state] -;; (when-let [interval (get state name)] -;; (util/clear-interval interval)) -;; (dissoc state name))}) - -(defn close-when-esc-or-outside - [state open? & {:keys [on-close]}] - (let [node (rum/dom-node state)] - (when open? - (listen state js/window "click" - (fn [e] - ;; If the click target is outside of current node - (when-not (dom/contains node (.. e -target)) - (on-close e)))) - - (listen state js/window "keydown" - (fn [e] - (case (.-keyCode e) - ;; Esc - 27 (on-close e) - nil)))))) - -(defn simple-close-listener - [state key] - (let [open? (get state key)] - (close-when-esc-or-outside state - open? - :on-close (fn [] - (reset! open? false))))) - -(defn event-mixin - [attach-listeners] - (merge - event-handler-mixin - {:did-mount (fn [state] - (attach-listeners state) - state) - :did-remount (fn [old-state new-state] - (detach old-state) - (attach-listeners new-state) - new-state)})) diff --git a/frontend/src/frontend/mui.cljs b/frontend/src/frontend/mui.cljs deleted file mode 100644 index 3d57ed760..000000000 --- a/frontend/src/frontend/mui.cljs +++ /dev/null @@ -1,114 +0,0 @@ -(ns frontend.mui - (:refer-clojure :exclude [list stepper]) - (:require [rum.core] - [frontend.rum :as r] - ["@material-ui/core" :refer [MuiThemeProvider]] - ["@material-ui/core/styles" :refer [createMuiTheme withStyles makeStyles]] - ["@material-ui/core/colors" :as colors] - ["@material-ui/core/CssBaseline" :default CssBaseline] - ["@material-ui/core/Typography" :default Typography] - ["@material-ui/core/Avatar" :default mui-avatar] - ["@material-ui/icons/Android" :default AndroidIcon] - ["@material-ui/core/AppBar" :default AppBar] - ["@material-ui/core/Divider" :default Divider] - ["@material-ui/core/Paper" :default Paper] - ["@material-ui/core/Toolbar" :default ToolBar] - ["@material-ui/core/IconButton" :default IconButton] - ["@material-ui/icons/Menu" :default MenuIcon] - ["@material-ui/core/Button" :default Button] - ["@material-ui/core/SwipeableDrawer" :default SwipeableDrawer] - ["@material-ui/core/Chip" :default Chip] - ["@material-ui/core/Fab" :default Fab] - ["@material-ui/core/List" :default List] - ["@material-ui/core/ListItem" :default ListItem] - ["@material-ui/core/ListItemText" :default ListItemText] - ["@material-ui/core/Container" :default Container] - ["@material-ui/core/Box" :default Box] - ["@material-ui/core/Snackbar" :default Snackbar] - ["@material-ui/core/Link" :default Link] - ["@material-ui/core/Checkbox" :default Checkbox] - ["@material-ui/core/Grid" :default Grid] - ["@material-ui/core/GridList" :default GridList] - ["@material-ui/core/Hidden" :default Hidden] - ;; ["@material-ui/core/Form" :default Form] - ["@material-ui/core/TextField" :default TextField] - ["@material-ui/core/TextareaAutosize" :default TextareaAutosize] - ["@material-ui/core/Card" :default Card] - ["@material-ui/core/CardActions" :default CardActions] - ["@material-ui/core/CardContent" :default CardContent] - ["@material-ui/core/CardHeader" :default CardHeader] - ["@material-ui/core/CardMedia" :default CardMedia] - ["@material-ui/core/Collapse" :default Collapse] - ["@material-ui/core/Avatar" :default Avatar] - ["@material-ui/core/CircularProgress" :default CircularProgress] - ["@material-ui/core/Badge" :default Badge] - ["@material-ui/core/Tooltip" :default Tooltip] - ["@material-ui/core/Dialog" :default Dialog] - ["@material-ui/core/DialogTitle" :default DialogTitle] - ["@material-ui/core/DialogContent" :default DialogContent] - ["@material-ui/core/DialogActions" :default DialogActions] - ["@material-ui/icons/Favorite" :default FavoriteIcon] - ["@material-ui/icons/Add" :default AddIcon] - ["@material-ui/icons/GitHub" :default GithubIcon] - ["@material-ui/icons/Share" :default ShareIcon] - ["@material-ui/icons/MoreVert" :default MoreVertIcon] - )) - -(defn custom-theme [] - (createMuiTheme - (clj->js - {:palette - {:type "light" - ;; :primary (.-purple colors) - ;; :secondary (.-green colors) - } - :typography - {:useNextVariants true}}))) - -(defonce theme-provider (r/adapt-class MuiThemeProvider)) -(defonce css-baseline (r/adapt-class CssBaseline)) -(defonce app-bar (r/adapt-class AppBar)) -(defonce divider (r/adapt-class Divider)) -(defonce tool-bar (r/adapt-class ToolBar)) -(defonce button (r/adapt-class Button)) -(defonce icon-button (r/adapt-class IconButton)) -(defonce typography (r/adapt-class Typography)) -(defonce container (r/adapt-class Container)) -(defonce box (r/adapt-class Box)) -(defonce snackbar (r/adapt-class Snackbar)) -(defonce link (r/adapt-class Link)) -(defonce checkbox (r/adapt-class Checkbox)) -(defonce grid (r/adapt-class Grid)) -(defonce grid-list (r/adapt-class GridList)) -(defonce paper (r/adapt-class Paper)) -(defonce collapse (r/adapt-class Collapse)) -(defonce avatar (r/adapt-class Avatar)) -(defonce favorite-icon (r/adapt-class FavoriteIcon)) -(defonce github-icon (r/adapt-class GithubIcon)) -(defonce add-icon (r/adapt-class AddIcon)) -(defonce fab (r/adapt-class Fab)) -(defonce share-icon (r/adapt-class ShareIcon)) -(defonce more-vert-icon (r/adapt-class MoreVertIcon)) -(defonce circular-progress (r/adapt-class CircularProgress)) -(defonce badge (r/adapt-class Badge)) -(defonce text-field (r/adapt-class TextField)) -(defonce textarea (r/adapt-class TextareaAutosize)) -(defonce tooltip (r/adapt-class Tooltip)) -(defonce dialog (r/adapt-class Dialog)) -(defonce dialog-title (r/adapt-class DialogTitle)) -(defonce dialog-content (r/adapt-class DialogContent)) -(defonce dialog-actions (r/adapt-class DialogActions)) -(defonce menu-icon (r/adapt-class MenuIcon)) -(defonce drawer (r/adapt-class SwipeableDrawer)) -(defonce chip (r/adapt-class Chip)) -(defonce list (r/adapt-class List)) -(defonce list-item (r/adapt-class ListItem)) -(defonce list-item-text (r/adapt-class ListItemText)) - -;; card -(defonce card (r/adapt-class Card)) -(defonce card-actions (r/adapt-class CardActions)) -(defonce card-content (r/adapt-class CardContent)) -(defonce card-actions (r/adapt-class CardActions)) -(defonce card-header (r/adapt-class CardHeader)) -(defonce card-media (r/adapt-class CardMedia)) diff --git a/frontend/src/frontend/page.cljs b/frontend/src/frontend/page.cljs index 3a10a884f..c7d76f138 100644 --- a/frontend/src/frontend/page.cljs +++ b/frontend/src/frontend/page.cljs @@ -1,15 +1,11 @@ (ns frontend.page - (:require [rum.core :as rum] - [frontend.layout :as layout] - [frontend.routes :as routes] - [frontend.state :as state] - [frontend.components.sidebar :as sidebar])) + (:require [uix.core.alpha :as uix] + [frontend.state :as state])) -(rum/defc current-page < rum/reactive +(defn current-page [] - (let [state (rum/react state/state) - current-page (get state :current-page :home)] - (sidebar/sidebar) - ;; (when-let [view (get routes/routes current-page)] - ;; (layout/frame (view) (:width state))) - )) + (let [route-match @(uix/state (:route-match @state/state))] + (prn "route-match: " route-match) + (if route-match + (when-let [view (:view (:data route-match))] + (view route-match))))) diff --git a/frontend/src/frontend/routes.cljs b/frontend/src/frontend/routes.cljs index dd1391eaf..aabad6b3f 100644 --- a/frontend/src/frontend/routes.cljs +++ b/frontend/src/frontend/routes.cljs @@ -1,10 +1,19 @@ (ns frontend.routes (:require [frontend.components.home :as home] - [frontend.components.settings :as settings] - [frontend.components.file :as file] - )) + [frontend.components.sidebar :as sidebar])) (def routes - {:home home/home - :settings settings/settings - :edit-file file/edit}) + [["/" + {:name :home + :view home/home + ;; :view sidebar/sidebar + }] + + ;; TODO: edit file + ;; Settings + ;; ["/item/:id" + ;; {:name ::item + ;; :view item-page + ;; :parameters {:path {:id int?} + ;; :query {(ds/opt :foo) keyword?}}}] + ]) diff --git a/frontend/src/frontend/rum.cljs b/frontend/src/frontend/rum.cljs deleted file mode 100644 index df04da016..000000000 --- a/frontend/src/frontend/rum.cljs +++ /dev/null @@ -1,59 +0,0 @@ -(ns frontend.rum - (:require [clojure.string :as s] - [clojure.set :as set] - [clojure.walk :as w])) - -;; copy from https://github.com/priornix/antizer/blob/35ba264cf48b84e6597743e28b3570d8aa473e74/src/antizer/core.cljs - -(defn kebab-case->camel-case - "Converts from kebab case to camel case, eg: on-click to onClick" - [input] - (let [words (s/split input #"-") - capitalize (->> (rest words) - (map #(apply str (s/upper-case (first %)) (rest %))))] - (apply str (first words) capitalize))) - -(defn map-keys->camel-case - "Stringifys all the keys of a cljs hashmap and converts them - from kebab case to camel case. If :html-props option is specified, - then rename the html properties values to their dom equivalent - before conversion" - [data & {:keys [html-props]}] - (let [convert-to-camel (fn [[key value]] - [(kebab-case->camel-case (name key)) value])] - (w/postwalk (fn [x] - (if (map? x) - (let [new-map (if html-props - (set/rename-keys x {:class :className :for :htmlFor}) - x)] - (into {} (map convert-to-camel new-map))) - x)) - data))) - -;; adapted from https://github.com/tonsky/rum/issues/20 -(defn adapt-class [react-class] - (fn [& args] - (let [[opts children] (if (map? (first args)) - [(first args) (rest args)] - [{} args]) - type# (first children) - ;; we have to make sure to check if the children is sequential - ;; as a list can be returned, eg: from a (for) - new-children (if (sequential? type#) - (let [result (sablono.interpreter/interpret children)] - (if (sequential? result) - result - [result])) - children) - ;; convert any options key value to a react element, if - ;; a valid html element tag is used, using sablono - vector->react-elems (fn [[key val]] - (if (sequential? val) - [key (sablono.interpreter/interpret val)] - [key val])) - new-options (into {} (map vector->react-elems opts))] - ;; (.dir js/console new-children) - (apply js/React.createElement react-class - ;; sablono html-to-dom-attrs does not work for nested hashmaps - (clj->js (map-keys->camel-case new-options :html-props true)) - new-children)))) diff --git a/frontend/src/frontend/state.cljs b/frontend/src/frontend/state.cljs index 4e88f0896..f8af0a323 100644 --- a/frontend/src/frontend/state.cljs +++ b/frontend/src/frontend/state.cljs @@ -22,4 +22,5 @@ :width nil :drawer? false :tasks {} + :route-match nil })) diff --git a/frontend/src/frontend/ui.cljs b/frontend/src/frontend/ui.cljs index 0f567ab35..d3a8e0186 100644 --- a/frontend/src/frontend/ui.cljs +++ b/frontend/src/frontend/ui.cljs @@ -1,23 +1,17 @@ (ns frontend.ui - (:require [rum.core :as rum] - [frontend.rum :as r] - ["react-transition-group" :refer [TransitionGroup CSSTransition]] + (:require ["react-transition-group" :refer [CSSTransition]] [frontend.util :as util] - [frontend.mixins :as mixins])) + [frontend.hooks :as hooks] + [uix.core.alpha :as uix])) -(defonce transition-group (r/adapt-class TransitionGroup)) -(defonce css-transition (r/adapt-class CSSTransition)) +(defn css-transition + [open? timeout state-fn] + [:> CSSTransition + {:in open? :timeout timeout} + (fn [state] + (uix/as-element (state-fn state)))]) -(defn css-transition-group - [css-options items] - (when (seq items) - (transition-group - (for [item items] - (css-transition - (merge {:key (cljs.core/random-uuid)} css-options) - item))))) - -(rum/defc dropdown-content-wrapper [state content] +(defn dropdown-content-wrapper [state content] [:div.origin-top-right.absolute.right-0.mt-2.w-48.rounded-md.shadow-lg {:class (case state "entering" "transition ease-out duration-100 transform opacity-0 scale-95" @@ -27,23 +21,22 @@ content]) ;; public exports -(rum/defcs dropdown < - (rum/local false ::show-dropdown?) - (mixins/event-mixin #(mixins/simple-close-listener % ::show-dropdown?)) - [state content] - (let [show-dropdown? (get state ::show-dropdown?)] - [:div.ml-3.relative +(defn dropdown + [content] + (let [ref (uix/ref nil) + open? (uix/state false)] + (hooks/setup-close-listener! ref open?) + [:div.ml-3.relative {:ref ref} [:div [:button.max-w-xs.flex.items-center.text-sm.rounded-full.focus:outline-none.focus:shadow-outline {:on-click (fn [] - (swap! show-dropdown? not))} + (swap! open? not))} [:img.h-8.w-8.rounded-full {:src "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"}]]] - (css-transition - {:in @show-dropdown? :timeout 0} - (fn [state] - (dropdown-content-wrapper state content)))])) + (css-transition @open? 0 + (fn [state] + (dropdown-content-wrapper state content)))])) (defn dropdown-with-links [links] diff --git a/frontend/src/frontend/util.cljs b/frontend/src/frontend/util.cljs index a887b6f2e..045e1038f 100644 --- a/frontend/src/frontend/util.cljs +++ b/frontend/src/frontend/util.cljs @@ -1,7 +1,8 @@ (ns frontend.util (:require [goog.object :as gobj] [promesa.core :as p] - [clojure.walk :as walk])) + [clojure.walk :as walk] + [clojure.string :as string])) (defn evalue [event] @@ -92,3 +93,10 @@ (->> (map (fn [entry] [(get entry k) entry]) col) (into {}))) + +;; ".lg:absolute.lg:inset-y-0.lg:right-0.lg:w-1/2" +(defn hiccup->class + [class] + (some->> (string/split class #"\.") + (string/join " ") + (string/trim)))