93 lines
2.8 KiB
Clojure
93 lines
2.8 KiB
Clojure
(ns examples.spinner
|
|
"Spinner example - demonstrates animated loading states.
|
|
Mirrors bubbletea's spinner example."
|
|
(:require [tui.core :as tui]
|
|
[tui.events :as ev]))
|
|
|
|
;; === Spinner Frames ===
|
|
(def spinner-styles
|
|
{:dots ["⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏"]
|
|
:line ["|" "/" "-" "\\"]
|
|
:circle ["◐" "◓" "◑" "◒"]
|
|
:square ["◰" "◳" "◲" "◱"]
|
|
:triangle ["◢" "◣" "◤" "◥"]
|
|
:bounce ["⠁" "⠂" "⠄" "⠂"]
|
|
:dots2 ["⣾" "⣽" "⣻" "⢿" "⡿" "⣟" "⣯" "⣷"]
|
|
:arc ["◜" "◠" "◝" "◞" "◡" "◟"]
|
|
:moon ["🌑" "🌒" "🌓" "🌔" "🌕" "🌖" "🌗" "🌘"]})
|
|
|
|
;; === Model ===
|
|
(def initial-model
|
|
{:frame 0
|
|
:style :dots
|
|
:loading true
|
|
:message "Loading..."
|
|
:styles (vec (keys spinner-styles))
|
|
:style-idx 0})
|
|
|
|
;; === Update ===
|
|
(defn update-fn [{:keys [model event]}]
|
|
(let [{:keys [styles style-idx]} model]
|
|
(cond
|
|
;; Quit
|
|
(or (ev/key= event \q)
|
|
(ev/key= event \c #{:ctrl}))
|
|
{:model model :events [(ev/quit)]}
|
|
|
|
;; Spinner frame - advance animation
|
|
(= (:type event) :spinner-frame)
|
|
(if (:loading model)
|
|
{:model (update model :frame inc)
|
|
:events [(ev/delayed-event 80 {:type :spinner-frame})]}
|
|
{:model model})
|
|
|
|
;; Space - simulate completion
|
|
(ev/key= event \space)
|
|
{:model (assoc model :loading false :message "Done!")}
|
|
|
|
;; Tab - change spinner style
|
|
(ev/key= event :tab)
|
|
(let [new-idx (mod (inc style-idx) (count styles))]
|
|
{:model (assoc model
|
|
:style-idx new-idx
|
|
:style (nth styles new-idx))})
|
|
|
|
;; r - restart
|
|
(ev/key= event \r)
|
|
{:model (assoc model :loading true :frame 0 :message "Loading...")
|
|
:events [(ev/delayed-event 80 {:type :spinner-frame})]}
|
|
|
|
:else
|
|
{:model model})))
|
|
|
|
;; === View ===
|
|
(defn spinner-view [{:keys [frame style]}]
|
|
(let [frames (get spinner-styles style)
|
|
idx (mod frame (count frames))]
|
|
(nth frames idx)))
|
|
|
|
(defn view [{:keys [loading message style] :as model} _size]
|
|
[:col {:gap 1}
|
|
[:box {:border :rounded :padding [1 2]}
|
|
[:col {:gap 1}
|
|
[:text {:bold true} "Spinner Demo"]
|
|
[:text ""]
|
|
[:row {:gap 1}
|
|
(if loading
|
|
[:text {:fg :cyan} (spinner-view model)]
|
|
[:text {:fg :green} "✓"])
|
|
[:text message]]
|
|
[:text ""]
|
|
[:text {:fg :gray} (str "Style: " (name style))]]]
|
|
[:col
|
|
[:text {:fg :gray} "tab: change style space: complete r: restart q: quit"]]])
|
|
|
|
;; === Main ===
|
|
(defn -main [& _args]
|
|
(println "Starting spinner...")
|
|
(tui/run {:init initial-model
|
|
:update update-fn
|
|
:view view
|
|
:init-events [(ev/delayed-event 80 {:type :spinner-frame})]})
|
|
(println "Spinner demo finished."))
|