Skip to content

Instantly share code, notes, and snippets.

@zeitstein
Last active February 20, 2022 16:21
Show Gist options
  • Save zeitstein/3fc2e1bde5610a8d6fba1c02cca74d2a to your computer and use it in GitHub Desktop.
Save zeitstein/3fc2e1bde5610a8d6fba1c02cca74d2a to your computer and use it in GitHub Desktop.
(ns grove.guide
(:require
[shadow.experiments.grove :as sg :refer (<< defc)]
[shadow.experiments.grove.runtime :as rt]
[shadow.experiments.grove.db :as db]
[shadow.experiments.grove.eql-query :as eql]
[shadow.experiments.grove.events :as ev]
[shadow.experiments.grove.local :as local]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; init db
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def schema
{::dir {:type :entity
:primary-key :dir/id
:attr {}
:joins {:dir/dirs [:many ::dir]
:dir/files [:many ::file]}}
::file {:type :entity
:primary-key :file/id
:attr {}}})
(def denormalized-data
{:dir/id 0 :dir/name "project" :dir/open? true
:dir/files [{:file/id 0 :file/name "deps.edn"}
{:file/id 1 :file/name ".nrepl-port" ::hidden? true}]
:dir/dirs [{:dir/id 1 :dir/name "src/example" :dir/open? true
:dir/files [{:file/id 2 :file/name "ui.cljs"}
{:file/id 3 :file/name "db.cljs"}]}]})
(defonce data-ref
(-> {} ;; empty db, we'll do a load! on init
(db/configure schema)
(atom)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; eql
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod eql/attr :dir/contains
[env db {:dir/keys [files dirs] :as current} query-part params]
(cond->> (concat dirs files)
(not (::show-hidden? db))
;; note: this will add these idents to list of things the containing query observes
;; i.e. changing something in these idents will cause the query to re-run
(filterv (fn [ident] (not (::hidden? (get db ident)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ui
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defc ui-file [ident]
(bind {:file/keys [name] ::keys [hidden?]}
(sg/query-ident ident))
(render
(<< [:div
[:span.font-mono {:class (when hidden? "text-gray-300")
:on-click {:e ::toggle-hide! :ident ident}}
name]])))
(defn expensive-computation [contains]
(count (filterv #(= ::file (first %)) contains)))
(defc ui-dir [ident]
(bind {:dir/keys [name open? contains] ::keys [hidden?] :as query-result}
(sg/query-ident ident [:dir/name :dir/open? :dir/contains ::hidden?]))
(bind num-visible
(expensive-computation contains))
(render
(<< [:div.border-l-2.border-black
[:span.font-bold.font-mono
{:on-click {:e ::dir-click! :ident ident}
:class (when hidden? "text-gray-300")}
name]
[:span.ml-3 (str "(visible loc: " (* num-visible 300) ")")]
(when (and open? (seq contains))
(<< [:div.m-3
(sg/keyed-seq contains identity
#(if (= (first %) ::dir)
(ui-dir %)
(ui-file %)))]))]))
(event ::dir-click! [env {:keys [ident]} e]
(if (.-ctrlKey e)
(sg/run-tx env {:e ::toggle-hide! :ident ident})
(sg/run-tx env {:e ::toggle-open! :ident ident}))))
(defc ui-root []
(bind {::keys [project-root show-hidden?]}
(sg/query-root [::project-root ::show-hidden?]))
(render
(<< [:h1.font-bold.text-xl
"click for open/close, ctrl+click for hide/unhide"]
[:button.bg-blue-500.hover:bg-blue-700.text-white.py-2.px-4.rounded
{:on-click {:e ::toggle-show-hidden! :show? (not show-hidden?)}
:style "margin: 10px 0;"}
(str "show hidden? " show-hidden?)]
(ui-dir (first project-root))))
(event ::toggle-hide! [env event-map event]
(when (.-ctrlKey event)
(sg/run-tx env event-map))))
(defonce rt-ref
(rt/prepare {} data-ref ::runtime-id))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; event handlers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(ev/reg-event rt-ref ::load!
(fn [tx-env ev-map]
(-> tx-env
(update :db db/add ::dir denormalized-data [::project-root])
(assoc-in [:db ::show-hidden?] true))))
(ev/reg-event rt-ref ::toggle-show-hidden!
(fn [tx-env {:keys [show?] :as event}]
(-> tx-env
(assoc-in [:db ::show-hidden?] show?)
(ev/queue-fx ::alert! event))))
(ev/reg-fx rt-ref ::alert!
(fn [fx-env {:keys [show?] :as fx-data}]
(js/alert (str "Will " (when-not show? "not") " show hidden files."))))
(ev/reg-event rt-ref ::toggle-open!
(fn [tx-env {:keys [ident]}]
(update-in tx-env [:db ident :dir/open?] not)))
(ev/reg-event rt-ref ::toggle-hide!
(fn [tx-env {:keys [ident] :as event-map}]
(update-in tx-env [:db ident ::hidden?] not)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; init
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def root-el
(js/document.getElementById "root"))
(defn ^:dev/after-load start []
(sg/render rt-ref root-el (ui-root)))
(defn init []
(local/init! rt-ref)
(sg/run-tx! rt-ref {:e ::load!})
(start))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment