From d72019d6bba70bc9152ea884bf252c7802d5ff82 Mon Sep 17 00:00:00 2001 From: Oly Date: Thu, 10 Aug 2023 14:35:50 +0100 Subject: [PATCH] Copy link to clipboard. --- resources/public/documents/about.org | 8 ++ resources/public/index.html | 7 ++ src/clojure_demo/components.cljs | 25 +++++ src/clojure_demo/core.cljs | 153 ++++++++++++++------------- src/clojure_demo/helpers.cljs | 36 +++++++ src/clojure_demo/state.cljs | 5 + 6 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 resources/public/documents/about.org create mode 100644 src/clojure_demo/components.cljs create mode 100644 src/clojure_demo/helpers.cljs create mode 100644 src/clojure_demo/state.cljs diff --git a/resources/public/documents/about.org b/resources/public/documents/about.org new file mode 100644 index 0000000..9fea39d --- /dev/null +++ b/resources/public/documents/about.org @@ -0,0 +1,8 @@ +#+TITLE: About +#+DESCRIPTION: Examples mostly oriented around web development + + +* Introduction +This site has various example's guides and snippets showing how to use Clojure and ClojureScript, they are mainly based on my journey learning coming from JavaScript and python having no previous experience with java or the JVM. + +Some of the struggles have been around Terminology & inter-op with the host languages along side re orientating my brain to think more functionally. diff --git a/resources/public/index.html b/resources/public/index.html index 17b082d..f8367e8 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -21,6 +21,13 @@ + clipboard slugify]])) + +(defn notification [] + (if (-> @site-state :notification) + [:div.fixed.p-5.top-20.right-0.border-2.border-sky-400.bg-white.opacity-100.transition-opacity.delay-150.duration-200 + (-> @site-state :notification)] + [:<>])) + +(defn header-on-click-copy-link [v] + {:on-click (fn [] (copy->clipboard (slugify v)))}) + +(defn header-lvl1 [v _] + [:h1.text-2xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} + [:a {:name (slugify v)} [:i.fa-solid.fa-link (header-on-click-copy-link v)] v]]) + +(defn header-lvl2 [v _] + [:h2.text-xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} + [:a {:name (slugify v)} [:i.fa-solid.fa-link (header-on-click-copy-link v)] v]]) + +(defn header-lvl3 [v _] + [:h3.text-base.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} + [:a {:name (slugify v)} [:i.fa-solid.fa-link (header-on-click-copy-link v)] v]]) diff --git a/src/clojure_demo/core.cljs b/src/clojure_demo/core.cljs index 8968155..2dfde1d 100644 --- a/src/clojure_demo/core.cljs +++ b/src/clojure_demo/core.cljs @@ -8,16 +8,17 @@ ["@nextjournal/lang-clojure" :refer [clojure]] ["react-dom/client" :refer [createRoot]] ["tarts" :as tarts] - [tick.core :as t] - [cljs.core.async :as async] - [cljs.core.async.interop :as async-in] - [shadow.cljs.modern :refer [js-await]] [ajax.core :refer [GET raw-response-format]] [cl-eorg.html :as h :refer [body headers org->replacements]] [cl-eorg.parser :as o :refer [parse]] [cl-eorg.themes.tachyon :refer [tachyon-theme]] - [clojure.string :as str] + [clojure-demo.components + :refer [header-lvl1 header-lvl2 header-lvl3 notification]] + [clojure-demo.helpers :refer [slugify]] + [clojure-demo.state :refer [site-state]] + [clojure-demo.tailwind-theme :refer [tailwind-theme]] [clojure.data.xml :as xml] + [clojure.string :as str] [honey.sql :as sql] [honey.sql.helpers :as sqlh] [reagent.core :as reagent] @@ -25,12 +26,12 @@ [reitit.frontend :as rf] [reitit.frontend.easy :as rfe] [reitit.frontend.history :refer [ignore-anchor-click?]] + [sci.configs.funcool.promesa :as sci-promesa] [sci.configs.reagent.reagent :as sci-reagent] [sci.configs.tonsky.datascript :as sci-datascript] - [sci.configs.funcool.promesa :as sci-promesa] [sci.core :as sci] - [clojure-demo.tailwind-theme :refer [tailwind-theme]] - [spec-tools.data-spec :as ds])) + [spec-tools.data-spec :as ds] + [tick.core :as t])) (def languages {"clojure" {:mode "clojure"} @@ -41,7 +42,8 @@ (def yaml-mode (LanguageSupport. (.define StreamLanguage yaml))) -(defn ^:sci/macro my-js-await [_ _ [name thenable] & body] +(defn ^:sci/macro my-js-await + [_ _ [name thenable] & body] (let [last-expr (last body) [body catch] (if (and (seq? last-expr) (= 'catch (first last-expr))) @@ -54,8 +56,7 @@ [`(.then (fn [~name] ~@body))]) ~@(when catch (let [[name & body] catch] - [`(.catch (fn [~name] ~@body))] - ))))) + [`(.catch (fn [~name] ~@body))]))))) ;; https://github.com/babashka/sci.configs (def rf-ns (sci/create-ns 'reitit.frontend nil)) @@ -112,14 +113,6 @@ ;(def sci-ctx (sci/empty-environment)) (sci/alter-var-root sci/print-fn (constantly *print-fn*)) -(defn slugify [s] - (when s - (str - (-> s - (str/lower-case) - (str/replace #"[^\w]+" "-") - (str/replace #"^-\\|-\\-$" ""))))) - (defn fetch-selected-text "Get the users selected text" [updated-view transactions] @@ -189,7 +182,7 @@ :parent @editor}))))) :component-will-unmount (fn [_] (.destroy @view)) :reagent-render (fn [] - (if (= exports "none") + (if (= exports "none") nil [:div.mb-8 [:div.ba.ma-0.f5.b--black-05.pa2.overflow-auto.editor {:ref #(reset! editor %)}] @@ -208,9 +201,9 @@ (def theme-tachyon (merge tachyon-theme {:SRC code-editor - :HEADER1 (fn [v _] [:h1 {:id (slugify v)} [:a {:name (slugify v)} v]]) - :HEADER2 (fn [v _] [:h2 {:id (slugify v)} [:a {:name (slugify v)} v]]) - :HEADER3 (fn [v _] [:h2 {:id (slugify v)} [:a {:name (slugify v)} v]]) + :HEADER1 header-lvl1 #_(fn [v _] [:h1 {:id (slugify v)} [:a {:name (slugify v)} v]]) + :HEADER2 header-lvl2 #_(fn [v _] [:h2 {:id (slugify v)} [:a {:name (slugify v)} v]]) + :HEADER3 header-lvl3 #_(fn [v _] [:h2 {:id (slugify v)} [:a {:name (slugify v)} v]]) :LINE :p.f5.f5-ns.lh-copy.mt0 :BULLETS :ul.mt0 :LINK :a #_link-handler})) @@ -218,42 +211,42 @@ (def theme (merge tailwind-theme {:SRC code-editor - :HEADER1 (fn [v _] [:h1.text-2xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]]) - :HEADER2 (fn [v _] [:h2.text-xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]]) - :HEADER3 (fn [v _] [:h2.text-base.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]]) + :HEADER1 header-lvl1 #_(fn [v _] [:h1.text-2xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]]) + :HEADER2 header-lvl2 #_(fn [v _] [:h2.text-xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]]) + :HEADER3 header-lvl3 #_(fn [v _] [:h2.text-base.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]]) :LINE :p.f5.f5-ns.lh-copy.mt0 :BULLETS :ul.mt0 :LINK :a #_link-handler})) #_(def theme-toc-tachyon - (merge tachyon-theme - {:HEADER1 (fn [v _] [:li [:a {:href (str "#" (slugify v))} - [:span.f4.fw6.f3-ns.lh-title.mt0.mb2 v]]]) - :HEADER2 (fn [v _] [:li.ml2 [:a {:href (str "#" (slugify v))} - [:span.f4.fw6.f4-ns.lh-title.mt0.mb2 v]]]) - :HEADER3 (fn [v _] [:li.ml4 [:a {:href (str "#" (slugify v))} - [:span.f5.fw6.f5-ns.lh-title.mt0.mb2 v]]]) - :HEADER4 (fn [v _] [:li.ml6 [:a {:href (str "#" (slugify v))} - [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]]) - :HEADER5 (fn [v _] [:li.ml8 [:a {:href (str "#" (slugify v))} - [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]]) - :HEADER6 (fn [v _] [:li.ml10 [:a {:href (str "#" (slugify v))} - [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])})) + (merge tachyon-theme + {:HEADER1 (fn [v _] [:li [:a {:href (str "#" (slugify v))} + [:span.f4.fw6.f3-ns.lh-title.mt0.mb2 v]]]) + :HEADER2 (fn [v _] [:li.ml2 [:a {:href (str "#" (slugify v))} + [:span.f4.fw6.f4-ns.lh-title.mt0.mb2 v]]]) + :HEADER3 (fn [v _] [:li.ml4 [:a {:href (str "#" (slugify v))} + [:span.f5.fw6.f5-ns.lh-title.mt0.mb2 v]]]) + :HEADER4 (fn [v _] [:li.ml6 [:a {:href (str "#" (slugify v))} + [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]]) + :HEADER5 (fn [v _] [:li.ml8 [:a {:href (str "#" (slugify v))} + [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]]) + :HEADER6 (fn [v _] [:li.ml10 [:a {:href (str "#" (slugify v))} + [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])})) (def theme-toc (merge tailwind-theme {:HEADER1 (fn [v _] [:li.list-decimal [:a.text-blue-800 {:class "hover:text-blue-500" :href (str "#" (slugify v))} - [:span.text-xl.fw6.lh-title.mt0.mb2 v]]]) + [:span.text-xl.fw6.lh-title.mt0.mb2 v]]]) :HEADER2 (fn [v _] [:li.list-decimal.ml-4 [:a.text-blue-800 {:href (str "#" (slugify v))} - [:span.text-lg.fw6.lh-title.mt0.mb2 v]]]) + [:span.text-lg.fw6.lh-title.mt0.mb2 v]]]) :HEADER3 (fn [v _] [:li.list-decimal.ml-8 [:a.text-blue-800 {:class "hover:text-blue-500" :href (str "#" (slugify v))} - [:span.text-base.fw6.lh-title.mt0.mb2 v]]]) + [:span.text-base.fw6.lh-title.mt0.mb2 v]]]) :HEADER4 (fn [v _] [:li.list-decimal.ml-12 [:a.text-blue-800 {:href (str "#" (slugify v))} - [:span.text-sm.fw6.lh-title.mt0.mb2 v]]]) + [:span.text-sm.fw6.lh-title.mt0.mb2 v]]]) :HEADER5 (fn [v _] [:li.list-decimal.ml-26 [:a.text-blue-800 {:href (str "#" (slugify v))} - [:span.text-xs.fw6.f5-ns.lh-title.mt0.mb2 v]]]) + [:span.text-xs.fw6.f5-ns.lh-title.mt0.mb2 v]]]) :HEADER6 (fn [v _] [:li.list-decimal.ml-30 [:a.hover:decoration-blue-400 {:href (str "#" (slugify v))} - [:span.text-xs.fw6.f5-ns.lh-title.mt0.mb2 v]]])})) + [:span.text-xs.fw6.f5-ns.lh-title.mt0.mb2 v]]])})) ;; put constant data here (def site-data @@ -318,8 +311,7 @@ :icon-image "https://avatars.githubusercontent.com/u/2181346?s=200&v=4"}]} :terminology {:title "Terminology" :intro "" - :key ::terminology} - } + :key ::terminology}} :demos {:hiccup-dsl-demo {:file "documents/hiccup-dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} @@ -340,9 +332,6 @@ :containers {:file "documents/containers.org" :git-link "https://github.com/atomjuice/containers"}}}) -;; Store site state -(defonce site-state (reagent/atom {})) - ;; form one component to render an article (defn article [{:keys [title description tagline]}] [:article.prose {:data-name "article-full-bleed-background"} @@ -420,7 +409,6 @@ :code (filter (fn filter-code [tag] (org-code (first tag))) dsl) :body (filter (fn filter-body [tag] (body (first tag))) dsl)}) - (defn home-page [] [:<> [articles @@ -438,19 +426,39 @@ (defn grouped-page [route] (let [group (keyword (name (:name (:data route))))] - [:<> - [articles - {:title (-> site-data :pages group :title) - :body (-> site-data :pages group :intro) - :articles - (mapv (fn fmt-map [demo] - {:title (:title demo) - :description (:description demo) - :link (rfe/href ::page {:page (:page demo)}) - :img-src (:icon-image demo)}) - (-> site-data :pages group :demos))}]])) + (swap! site-state dissoc :notification) + (fn [route] + [:<> + [articles + {:title (-> site-data :pages group :title) + :body (-> site-data :pages group :intro) + :articles + (mapv (fn fmt-map [demo] + {:title (:title demo) + :description (:description demo) + :link (rfe/href ::page {:page (:page demo)}) + :img-src (:icon-image demo)}) + (-> site-data :pages group :demos))}]]))) (defn default-page [route] + (let [demo-key (keyword (-> route :parameters :path :page)) + content (reagent/atom {})] + (GET (str "/" (-> site-data :demos demo-key :file)) + {:response-format (raw-response-format) + :handler (fn [response] + (->> response + parse + org->split2 + (reset! content)))}) + (fn [route] + (if @content + [:main + [:h1.mt-8.mb-8.text-4xl (:content (last (first (:header @content))))] + [:div + (into [:div] (org->replacements theme (:body @content)))]] + [:<>])))) + +(defn default-page-header [route] (let [demo-key (keyword (-> route :parameters :path :page)) content (reagent/atom {})] (GET (str "/" (-> site-data :demos demo-key :file)) @@ -483,11 +491,10 @@ (into [:div] (org->replacements theme (:body @content)))]] [:<>])))) - - ;; form one render about page component (defn about-page [] - [:div + [default-page {:parameters {:path {:page "about"}}}] + #_[:div [:section.mw7.center.avenir [:h1 "Clojure library examples to aid learning"] [:p ""] @@ -504,9 +511,9 @@ ["/page/:page" {:name ::page - :view default-page - :parameters {:path {:page string?} - :query {(ds/opt :foo) keyword?}}}] + :view default-page-header + :parameters {:path {:page string?} + :query {(ds/opt :foo) keyword?}}}] ["/terminology/" {:name ::terminology @@ -530,21 +537,15 @@ ["/about/" {:name ::about - :view about-page}] - - #_["/demo/:page" - {:name ::demo - :view demo-page - :parameters {:path {:page string?} - :query {(ds/opt :foo) keyword?}}}]]) + :view about-page}]]) ;; top level component contains nav and adds in the select page into a containing element ;; we are adding in a style sheet but this will often be done in index.html (defn current-page [] [:div + [notification] [navbar (concat [{:href (rfe/href ::frontpage) :title "title here" :text "home" :key "homepage"}] - (mapv (fn build-nav [[key page]] {:href (rfe/href (:key page)) :text (:title page)}) (-> site-data :pages)) [;;{:href (rfe/href ::dsl) :text "DSL's" :key "dsl"} {:title "Domain Specific Languages" :href (rfe/href ::about) :text "About" :key "about"} diff --git a/src/clojure_demo/helpers.cljs b/src/clojure_demo/helpers.cljs new file mode 100644 index 0000000..e239f2d --- /dev/null +++ b/src/clojure_demo/helpers.cljs @@ -0,0 +1,36 @@ +(ns clojure-demo.helpers + (:require + [cljs.core.async :refer [go]] + [cljs.core.async.interop :refer [ s + (str/lower-case) + (str/replace #"[^\w]+" "-") + (str/replace #"^-\\|-\\-$" ""))))) + +(defn build-page-path [title] + (str "http://127.0.0.1:8080/" #_"https://clojure-demos.digitaloctave.com/" "page/" + (when (-> @site-state :current-route :parameters :path :page) + (-> @site-state :current-route :parameters :path :page)) + "#" title) + ) + + +(defn copy->clipboard + "Simple wrapper which copies text to the clipboard and resolves the promise" + [text] + (go (clipboard "test2")) diff --git a/src/clojure_demo/state.cljs b/src/clojure_demo/state.cljs new file mode 100644 index 0000000..959b053 --- /dev/null +++ b/src/clojure_demo/state.cljs @@ -0,0 +1,5 @@ +(ns clojure-demo.state + (:require [reagent.core :as reagent])) + +;; Store site state +(defonce site-state (reagent/atom {}))