mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			147 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Clojure
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Clojure
		
	
	
	
	
	
| ;; Copyright (c) Alan Dipert and Micha Niskin. All rights reserved.
 | |
| ;; The use and distribution terms for this software are covered by the
 | |
| ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 | |
| ;; which can be found in the file epl-v10.html at the root of this distribution.
 | |
| ;; By using this software in any fashion, you are agreeing to be bound by
 | |
| ;; the terms of this license.
 | |
| ;; You must not remove this notice, or any other, from this software.
 | |
| 
 | |
| (page "index.html"
 | |
|   (:refer-clojure :exclude [nth])
 | |
|   (:require
 | |
|    [tailrecursion.hoplon.reload        :refer [reload-all]]
 | |
|    [tailrecursion.hoplon.util          :refer [nth name pluralize]]
 | |
|    [tailrecursion.hoplon.storage-atom  :refer [local-storage]]))
 | |
| 
 | |
| ;; utility functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| 
 | |
| (declare route state editing)
 | |
| 
 | |
| (reload-all)
 | |
| 
 | |
| (def mapvi  (comp vec map-indexed))
 | |
| 
 | |
| (defn dissocv [v i]
 | |
|   (let [z (- (dec (count v)) i)]
 | |
|     (cond (neg?  z) v
 | |
|           (zero? z) (pop v)
 | |
|           (pos?  z) (into (subvec v 0 i) (subvec v (inc i))))))
 | |
| 
 | |
| (defn decorate [todo route editing i]
 | |
|   (let [{done? :completed text :text} todo]
 | |
|     (-> todo (assoc :editing (= editing i)
 | |
|                     :visible (and (not (empty? text))
 | |
|                                   (or (= "#/" route)
 | |
|                                       (and (= "#/active" route) (not done?))
 | |
|                                       (and (= "#/completed" route) done?)))))))
 | |
| 
 | |
| ;; persisted state cell (AKA: stem cell) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| 
 | |
| (def   state        (-> (cell []) (local-storage ::store)))
 | |
| 
 | |
| ;; local state cells ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| 
 | |
| (defc  loaded?      false)
 | |
| (defc  editing      nil)
 | |
| (def   route        (route-cell "#/"))
 | |
| 
 | |
| ;; formula cells (computed state) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| 
 | |
| (defc= completed    (filter :completed state))
 | |
| (defc= active       (remove :completed state))
 | |
| (defc= plural-item  (pluralize "item" (count active)))
 | |
| (defc= todos        (mapvi #(list %1 (decorate %2 route editing %1)) state))
 | |
| 
 | |
| ;; state transition functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| 
 | |
| (defn  todo        [t]   {:completed false :text t})
 | |
| (defn  destroy!    [i]   (swap! state dissocv i))
 | |
| (defn  done!       [i v] (swap! state assoc-in [i :completed] v))
 | |
| (defn  clear-done! [& _] (swap! state #(vec (remove :completed %))))
 | |
| (defn  new!        [t]   (when (not (empty? t)) (swap! state conj (todo t))))
 | |
| (defn  all-done!   [v]   (swap! state #(mapv (fn [x] (assoc x :completed v)) %)))
 | |
| (defn  editing!    [i v] (reset! editing (if v i nil)))
 | |
| (defn  text!       [i v] (if (empty? v) (destroy! i) (swap! state assoc-in [i :text] v)))
 | |
| 
 | |
| ;; page ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 | |
| 
 | |
| (html :lang "en"
 | |
|   (head
 | |
|     (meta :charset "utf-8")
 | |
|     (meta :http-equiv "X-UA-Compatible" :content "IE=edge,chrome=1")
 | |
|     (link :rel "stylesheet" :href "base.css")
 | |
|     (title "Hoplon • TodoMVC"))
 | |
|   (body
 | |
|     (noscript
 | |
|       (div :id "noscript"
 | |
|         (p "JavaScript is required to view this page.")))
 | |
|     (div
 | |
|       (section :id "todoapp"
 | |
|         (header :id "header"
 | |
|           (h1 "todos")
 | |
|           (form :on-submit #(do (new! (val-id :new-todo))
 | |
|                                 (do! (by-id :new-todo) :value ""))
 | |
|             (input
 | |
|               :id "new-todo"
 | |
|               :type "text"
 | |
|               :autofocus true
 | |
|               :placeholder "What needs to be done?"
 | |
|               :on-blur #(do! (by-id :new-todo) :value ""))))
 | |
|         (section
 | |
|           :id "main"
 | |
|           :do-toggle (cell= (not (and (empty? active) (empty? completed))))
 | |
|           (input
 | |
|             :id "toggle-all"
 | |
|             :type "checkbox"
 | |
|             :do-attr (cell= {:checked (empty? active)}) 
 | |
|             :on-click #(all-done! (val-id :toggle-all)))
 | |
|           (label :for "toggle-all"
 | |
|             "Mark all as complete")
 | |
|           (ul :id "todo-list"
 | |
|             (loop-tpl
 | |
|               :reverse true
 | |
|               :bind-ids [done# edit#]
 | |
|               :bindings [[i {edit? :editing done? :completed todo-text :text show? :visible}] todos] 
 | |
|               (li
 | |
|                 :do-class (cell= {:completed done? :editing edit?}) 
 | |
|                 :do-toggle show?
 | |
|                 (div :class "view" :on-dblclick #(editing! @i true)
 | |
|                   (input
 | |
|                     :id done# 
 | |
|                     :type "checkbox"
 | |
|                     :class "toggle"
 | |
|                     :do-attr (cell= {:checked done?}) 
 | |
|                     :on-click #(done! @i (val-id done#)))
 | |
|                   (label (text "~{todo-text}"))
 | |
|                   (button
 | |
|                     :type "submit"
 | |
|                     :class "destroy"
 | |
|                     :on-click  #(destroy! @i)))
 | |
|                 (form :on-submit #(editing! @i false)
 | |
|                   (input
 | |
|                     :id edit#
 | |
|                     :type "text"
 | |
|                     :class "edit"
 | |
|                     :do-value todo-text
 | |
|                     :do-focus edit?
 | |
|                     :on-blur #(when @edit? (editing! @i false))
 | |
|                     :on-change #(when @edit? (text! @i (val-id edit#)))))))))
 | |
|         (footer 
 | |
|           :id "footer"
 | |
|           :do-toggle (cell= (not (and (empty? active) (empty? completed))))
 | |
|           (span :id "todo-count"
 | |
|             (strong (text "~(count active) "))
 | |
|             (span (text "~{plural-item} left")))
 | |
|           (ul :id "filters"
 | |
|             (li (a :href "#/"          :do-class (cell= {:selected (= "#/" route)})          "All"))
 | |
|             (li (a :href "#/active"    :do-class (cell= {:selected (= "#/active" route)})    "Active"))
 | |
|             (li (a :href "#/completed" :do-class (cell= {:selected (= "#/completed" route)}) "Completed")))
 | |
|           (button
 | |
|             :type      "submit"
 | |
|             :id        "clear-completed"
 | |
|             :on-click  #(clear-done!)
 | |
|             (text "Clear completed (~(count completed))"))))
 | |
|       (footer :id "info" 
 | |
|         (p "Double-click to edit a todo")
 | |
|         (p "Part of " (a :href "http://github.com/tailrecursion/hoplon-demos/" "hoplon-demos")))))) 
 |