From 1ae3e950117c42a89144d3fca7fa9187be07db74 Mon Sep 17 00:00:00 2001 From: Denis Fuenzalida Date: Wed, 4 Nov 2020 19:13:49 -0800 Subject: [PATCH] Updated to recent Reagent and Fluent UI (formerly Fabric) --- README.md | 53 +++++++++++++++++++++++++-------- assets/index.html | 2 +- package.json | 52 ++++++++++++++++---------------- shadow-cljs.edn | 11 +++---- src/fabric_todos/core.cljs | 29 ++++++++++-------- src/fabric_todos/fabric.cljs | 20 ------------- src/fabric_todos/footer.cljs | 8 ++--- src/fabric_todos/header.cljs | 26 ++++++++-------- src/fabric_todos/todo_list.cljs | 22 +++++++------- 9 files changed, 117 insertions(+), 106 deletions(-) delete mode 100644 src/fabric_todos/fabric.cljs diff --git a/README.md b/README.md index 8c6f503..8469aa0 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ # Fabric TODOs -An implementation of the classical TODO MVC application using ClojureScript, [Reagent](http://reagent-project.github.io/), React and [Microsoft UI Fabric React](https://developer.microsoft.com/en-us/fabric#/get-started). +An implementation of the classical TODO MVC application using ClojureScript, [Reagent](http://reagent-project.github.io/), React and [Microsoft Fluent UI](https://fluent-ui.com). Based on the UI Fabric component library demo from the Microsoft [Frontend Bootcamp](https://microsoft.github.io/frontend-bootcamp/step2-02/demo/). ### Requirements * [Java](https://adoptopenjdk.net/) -* [Leiningen](https://leiningen.org/) * [NodeJS](https://nodejs.org/) +* [Shadow-cljs](https://shadow-cljs.org/) +* [Yarn](https://yarnpkg.com/) ### Screenshot @@ -18,29 +19,55 @@ Based on the UI Fabric component library demo from the Microsoft [Frontend Bootc Navigate to the project folder and run the following commands in the terminal. -To download both the Clojure and NodeJS dependencies run: +To download the NodeJS dependencies run: ``` -lein deps +yarn install ``` -To start the Figwheel compiler run the following command: +Copy the static HTML file to the target folder with: ``` -lein figwheel +yarn html ``` -Figwheel will automatically push cljs changes to the browser. -Once Figwheel starts up, you should be able to open the `public/index.html` page in the browser. +To start the compiler in watch mode: + +``` +yarn watch +``` + +Shadow-cljs will automatically push cljs changes to the browser. +Once the ClojureScript code is compiled, visit http://localhost:8080/ ### REPL -The project is setup to start nREPL on port `7002` once Figwheel starts. -Once you connect to the nREPL, run `(cljs)` to switch to the ClojureScript REPL. +On watch mode a nREPL will be started on port 37117: + +``` +$ yarn watch +yarn run v1.22.10 +$ shadow-cljs watch app +shadow-cljs - config: /home/denis/Projects/ClojureScript/fabric-todos/shadow-cljs.edn +shadow-cljs - HTTP server available at http://localhost:8080 +shadow-cljs - server version: 2.11.7 running at http://localhost:9630 +shadow-cljs - nREPL server started on port 37117 +shadow-cljs - watching build :app +``` -### Building for production +Once you connect to the nREPL, you can run functions from the REPL, like the following bit to create a new TODO: ``` -lein clean -lein package +(ns fabric-todos.state) ;; Change to the namespace where 'add-todo' is defined + +(add-todo "This task was created from the REPL") ``` + +You should see the new task created at the end of the list. + + +### License + +MIT + + diff --git a/assets/index.html b/assets/index.html index 331e7c6..3cd371b 100644 --- a/assets/index.html +++ b/assets/index.html @@ -4,7 +4,7 @@ - +
diff --git a/package.json b/package.json index a3d8fb0..5d89b9d 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,29 @@ { - "name": "fabric-todos", - "version": "1.0.0", - "description": "TODOs application in CLJS, Reagent and Microsoft UI Fabric React", - "main": "index.js", - "scripts": { - "watch": "shadow-cljs watch app", - "compile": "shadow-cljs compile app", - "release": "shadow-cljs release app", - "html": "mkdir -p target && cp assets/index.html target/", - "serve": "yarn html && http-server target/", - "del": "rm -r target/*", - "build": "yarn release && yarn html && yarn serve" - }, - "author": "Denis Fuenzalida", - "license": "MIT", - "devDependencies": { - "http-server": "^0.11.1", - "shadow-cljs": "^2.8.40" - }, - "dependencies": { - "create-react-class": "^15.6.3", - "office-ui-fabric-react": "^6.195.3", - "react": "^16.8.6", - "react-dom": "^16.8.6" - } + "name": "fabric-todos", + "version": "1.0.0", + "description": "TODOs application in ClojureScript, Reagent and Microsoft Fluent UI", + "main": "index.js", + "license": "MIT", + "devDependencies": { + "http-server": "^0.12.3", + "shadow-cljs": "^2.11.7" + }, + "dependencies": { + "@fluentui/react": "^7.149.3", + "@uifabric/icons": "^7.5.15", + "create-react-class": "^15.7.0", + "react": "^16.13.0", + "react-dom": "^16.13.0" + }, + "author": "Denis Fuenzalida", + "license": "MIT", + "scripts": { + "watch": "shadow-cljs watch app", + "compile": "shadow-cljs compile app", + "release": "shadow-cljs release app", + "html": "mkdir -p target && cp assets/index.html target/", + "serve": "yarn html && http-server target/", + "del": "rm -r target/*", + "build": "yarn release && yarn html && yarn serve" + } } diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 6cbf7e7..4ab97af 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -1,10 +1,7 @@ {:source-paths ["src"] - :dependencies [[reagent "0.8.1"] - [thheller/shadow-cljsjs "0.0.18"]] + :dependencies [[reagent "1.0.0-alpha2"]] :dev-http {8080 "target/"} - :builds {:app {:output-dir "target/" + :builds {:app {:target :browser + :output-dir "target" :asset-path "." - :target :browser - :compiler-options {:infer-externs :auto} - :modules {:main {:init-fn fabric-todos.core/init!}} - :devtools {:after-load fabric-todos.core/reload!}}}} + :modules {:main {:init-fn fabric-todos.core/main!}}}}} diff --git a/src/fabric_todos/core.cljs b/src/fabric_todos/core.cljs index 234eab0..ad0a311 100644 --- a/src/fabric_todos/core.cljs +++ b/src/fabric_todos/core.cljs @@ -1,15 +1,16 @@ (ns fabric-todos.core - (:require [fabric-todos.fabric :as fab] + (:require ["@fluentui/react" :as f] + ["@uifabric/icons" :as ui] + [reagent.dom :as rdom] [fabric-todos.header :as header] [fabric-todos.todo-list :as todo-list] - [fabric-todos.footer :as footer] - [reagent.core :as r])) + [fabric-todos.footer :as footer])) ;; Main application ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn todo-app [] - [:> fab/Stack {:horizontalAlign "center"} - [:> fab/Stack {:style {:width 400} :gap 25} + [:> f/Stack {:horizontalAlign "center"} + [:> f/Stack {:style {:width 400} :tokens {:childrenGap 25}} (header/todo-header) (todo-list/todo-list) (footer/todo-footer)]]) @@ -17,12 +18,16 @@ ;; App initialization ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn mount-root [] - (r/render [todo-app] (.getElementById js/document "app"))) + (rdom/render [todo-app] (.getElementById js/document "app"))) -(defn init! [] - (mount-root) - (fab/init-icons!) - (header/focus-new-todo)) +(defn init-ui [] + (mount-root)) -(defn reload! [] - (println "reloading...")) +(defn main! [] + (println "starting...") + (ui/initializeIcons) + (init-ui)) + +(defn ^:dev/after-load reload! [] + (println "reloading...") + (init-ui)) diff --git a/src/fabric_todos/fabric.cljs b/src/fabric_todos/fabric.cljs deleted file mode 100644 index b50582f..0000000 --- a/src/fabric_todos/fabric.cljs +++ /dev/null @@ -1,20 +0,0 @@ -(ns fabric-todos.fabric - (:require ["office-ui-fabric-react" :as Fabric])) - -;; Fabric elements ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(def Checkbox Fabric/Checkbox) -(def DefaultButton Fabric/DefaultButton) -(def IconButton Fabric/IconButton) -(def Rating Fabric/Rating) -(def Pivot Fabric/Pivot) -(def PivotItem Fabric/PivotItem) -(def PrimaryButton Fabric/PrimaryButton) -(def Text Fabric/Text) -(def TextField Fabric/TextField) -(def Stack Fabric/Stack) - -(defn init-icons! - "Load the icon fonts. See https://github.com/OfficeDev/office-ui-fabric-react/wiki/Using-icons" - [] - (Fabric/initializeIcons)) diff --git a/src/fabric_todos/footer.cljs b/src/fabric_todos/footer.cljs index 3d35ab4..9d35192 100644 --- a/src/fabric_todos/footer.cljs +++ b/src/fabric_todos/footer.cljs @@ -1,10 +1,10 @@ (ns fabric-todos.footer - (:require [fabric-todos.fabric :as fab] + (:require ["@fluentui/react" :as f] [fabric-todos.state :as state])) (defn todo-footer [] (let [remaining (count (state/remaining-todos)) label (str remaining " item" (when-not (= 1 remaining) "s") " left")] - [:> fab/Stack {:horizontal true :horizontalAlign "space-between"} - [:> fab/Text label] - [:> fab/DefaultButton {:onClick state/clear-completed} "Clear Completed"]])) + [:> f/Stack {:horizontal true :horizontalAlign "space-between"} + [:> f/Text label] + [:> f/DefaultButton {:onClick state/clear-completed} "Clear Completed"]])) diff --git a/src/fabric_todos/header.cljs b/src/fabric_todos/header.cljs index 7d6f760..1fd585d 100644 --- a/src/fabric_todos/header.cljs +++ b/src/fabric_todos/header.cljs @@ -1,6 +1,6 @@ (ns fabric-todos.header (:require [clojure.string :refer [blank?]] - [fabric-todos.fabric :as fab] + ["@fluentui/react" :as f] [fabric-todos.state :as state])) (defn focus-new-todo [] @@ -20,19 +20,19 @@ (focus-new-todo)))) (defn todo-header [] - [:> fab/Stack - [:> fab/Stack {:horizontal true :horizontalAlign "center"} - [:> fab/Stack.Item {:align "center"} - [:> fab/Text {:variant "xxLarge"} "todos"]]] - [:> fab/Stack {:horizontal "horizontal"} - [:> fab/Stack.Item {:grow true} - [:> fab/TextField {:id "newTodo" + [:> f/Stack + [:> f/Stack {:horizontal true :horizontalAlign "center"} + [:> f/Stack.Item {:align "center"} + [:> f/Text {:variant "xxLarge"} "todos"]]] + [:> f/Stack {:horizontal "horizontal"} + [:> f/Stack.Item {:grow true} + [:> f/TextField {:id "newTodo" :placeholder "What needs to be done?" :value (state/new-todo-value) :onKeyDown #(when (= 13 (.-which %)) (add-btn-handler)) :onChange textfield-change}]] - [:> fab/PrimaryButton {:onClick add-btn-handler} "Add"]] - [:> fab/Pivot {:onLinkClick pivot-filter} - [:> fab/PivotItem {:headerText "all"}] - [:> fab/PivotItem {:headerText "active"}] - [:> fab/PivotItem {:headerText "completed"}]]]) + [:> f/PrimaryButton {:onClick add-btn-handler} "Add"]] + [:> f/Pivot {:onLinkClick pivot-filter} + [:> f/PivotItem {:headerText "all"}] + [:> f/PivotItem {:headerText "active"}] + [:> f/PivotItem {:headerText "completed"}]]]) diff --git a/src/fabric_todos/todo_list.cljs b/src/fabric_todos/todo_list.cljs index 9ba72db..5f9a6c4 100644 --- a/src/fabric_todos/todo_list.cljs +++ b/src/fabric_todos/todo_list.cljs @@ -1,28 +1,28 @@ (ns fabric-todos.todo-list - (:require [fabric-todos.fabric :as fab] + (:require ["@fluentui/react" :as f] [fabric-todos.state :as state] [reagent.core :refer [atom]])) (defn todo-item [{:keys [id text editing done] :as item}] [:div {:key (str "stack" id)} - [:> fab/Stack {:horizontal true :horizontalAlign "space-between" :verticalAlign "center"} + [:> f/Stack {:horizontal true :horizontalAlign "space-between" :verticalAlign "center"} (if editing (let [text-value (atom text) update-fn (fn [ev val] (reset! text-value val))] - [:> fab/Stack.Item {:grow true} - [:> fab/Stack {:horizontal true} - [:> fab/Stack.Item {:grow true} - [:> fab/TextField {:value @text-value :onChange update-fn :onKeyDown update-fn}]] - [:> fab/DefaultButton {:onClick #(state/update-todo id @text-value)} "save"]]]) + [:> f/Stack.Item {:grow true} + [:> f/Stack {:horizontal true} + [:> f/Stack.Item {:grow true} + [:> f/TextField {:value @text-value :onChange update-fn :onKeyDown update-fn}]] + [:> f/DefaultButton {:onClick #(state/update-todo id @text-value)} "save"]]]) ;; else [:<> - [:> fab/Checkbox {:label text :checked done :onChange #(state/toggle-done id)}] + [:> f/Checkbox {:label text :checked done :onChange #(state/toggle-done id)}] [:div - [:> fab/IconButton {:iconProps {:iconName "Edit"} :className "clearButton" :onClick #(state/edit-todo id)}] - [:> fab/IconButton {:iconProps {:iconName "Cancel"} :className "clearButton" :onClick #(state/delete-todo id)}]]]) + [:> f/IconButton {:iconProps {:iconName "Edit"} :className "clearButton" :onClick #(state/edit-todo id)}] + [:> f/IconButton {:iconProps {:iconName "Cancel"} :className "clearButton" :onClick #(state/delete-todo id)}]]]) ]]) (defn todo-list [] - [:> fab/Stack {:gap 10} + [:> f/Stack {:tokens {:childrenGap 10}} (map todo-item (state/filtered-todos))])