Files
clojure-tui/examples/views.clj
2026-02-03 13:01:10 -05:00

119 lines
3.3 KiB
Clojure

(ns examples.views
"Multiple views example - demonstrates state machine pattern.
Mirrors bubbletea's views example."
(:require [tui.core :as tui]
[tui.events :as ev]))
;; === Model ===
(def initial-model
{:view :menu ; :menu, :detail, :confirm
:cursor 0
:items [{:name "Profile" :desc "View and edit your profile settings"}
{:name "Settings" :desc "Configure application preferences"}
{:name "Help" :desc "Get help and documentation"}
{:name "About" :desc "About this application"}]
:selected nil})
;; === Update ===
(defn update-fn [{:keys [model event]}]
(let [{:keys [view cursor items]} model]
(case view
;; Menu view
:menu
(cond
(or (ev/key= event \q)
(ev/key= event \c #{:ctrl}))
{:model model :events [(ev/quit)]}
(or (ev/key= event :up)
(ev/key= event \k))
{:model (update model :cursor #(max 0 (dec %)))}
(or (ev/key= event :down)
(ev/key= event \j))
{:model (update model :cursor #(min (dec (count items)) (inc %)))}
(ev/key= event :enter)
{:model (assoc model
:view :detail
:selected (nth items cursor))}
:else
{:model model})
;; Detail view
:detail
(cond
(or (ev/key= event \q)
(ev/key= event \c #{:ctrl}))
{:model (assoc model :view :confirm)}
(or (ev/key= event :escape)
(ev/key= event \b))
{:model (assoc model :view :menu :selected nil)}
:else
{:model model})
;; Confirm quit dialog
:confirm
(cond
(ev/key= event \y)
{:model model :events [(ev/quit)]}
(or (ev/key= event \n)
(ev/key= event :escape))
{:model (assoc model :view :detail)}
:else
{:model model}))))
;; === Views ===
(defn menu-view [{:keys [cursor items]}]
[:col {:gap 1}
[:box {:border :rounded :padding [0 1] :title "Main Menu"}
[:col
(for [[idx item] (map-indexed vector items)]
(let [is-cursor (= idx cursor)]
[:row {:gap 1}
[:text {:fg (when is-cursor :cyan)} (if is-cursor ">" " ")]
[:text {:bold is-cursor
:fg (when is-cursor :cyan)}
(:name item)]]))]]
[:text {:fg :gray} "j/k: navigate enter: select q: quit"]])
(defn detail-view [{:keys [selected]}]
[:col {:gap 1}
[:box {:border :double :padding [1 2]}
[:col {:gap 1}
[:text {:bold true :fg :cyan} (:name selected)]
[:text ""]
[:text (:desc selected)]]]
[:text {:fg :gray} "b/esc: back q: quit"]])
(defn confirm-view [_model]
[:col {:gap 1}
[:box {:border :rounded :padding [1 2]}
[:col
[:text {:bold true :fg :yellow} "Quit?"]
[:text ""]
[:text "Are you sure you want to quit?"]
[:text ""]
[:row {:gap 2}
[:text {:fg :green} "[y] Yes"]
[:text {:fg :red} "[n] No"]]]]])
(defn view [{:keys [view] :as model}]
(case view
:menu (menu-view model)
:detail (detail-view model)
:confirm (confirm-view model)))
;; === Main ===
(defn -main [& _args]
(println "Starting views demo...")
(tui/run {:init initial-model
:update update-fn
:view view})
(println "Views demo finished."))