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))])