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