charm.clj provides reusable UI components that follow the Elm Architecture pattern. Each component has a consistent API with init, update, and view functions.
All components follow this structure:
;; 1. Create a component
(def my-component (component-name options...))
;; 2. Initialize (returns [component cmd])
(let [[component cmd] (component-init my-component)]
;; Use component and execute cmd if non-nil
)
;; 3. Update (returns [new-component cmd])
(let [[new-component cmd] (component-update component msg)]
;; Use new-component and execute cmd if non-nil
)
;; 4. View (returns string)
(component-view component)
Components integrate with charm's program loop which handles:
(require '[charm.core :as charm])
(defn init []
[{:spinner (charm/spinner :dots)}
nil])
(defn update-fn [state msg]
(let [[new-spinner cmd] (charm/spinner-update (:spinner state) msg)]
[(assoc state :spinner new-spinner) cmd]))
(defn view [state]
(str "Loading " (charm/spinner-view (:spinner state))))
(charm/run {:init init
:update update-fn
:view view})
| Component | Description | Tick-based |
|---|---|---|
| spinner | Animated loading indicators | Yes |
| text-input | Text entry with cursor editing | No |
| list | Scrollable item selection | No |
| paginator | Page navigation indicators | No |
| timer | Countdown/count-up timer | Yes |
| progress | Progress bar display | No |
| help | Keyboard shortcut display | No |
Components like spinner and timer use asynchronous tick commands to animate. These components:
init that starts the tick loopupdate to continue the animation;; Spinner sends tick messages to itself
(let [[spinner cmd] (charm/spinner-init my-spinner)]
;; cmd will trigger a :spinner-tick message after the interval
)
Multiple components can be combined in a single application:
(defn init []
[{:input (charm/text-input :prompt "Search: ")
:list (charm/item-list items)
:help (charm/help bindings)}
nil])
(defn update-fn [state msg]
(cond
;; Route to text-input when focused
(:focused (:input state))
(let [[input cmd] (charm/text-input-update (:input state) msg)]
[(assoc state :input input) cmd])
;; Otherwise route to list
:else
(let [[list cmd] (charm/list-update (:list state) msg)]
[(assoc state :list list) cmd])))
(defn view [state]
(str (charm/text-input-view (:input state)) "\n"
(charm/list-view (:list state)) "\n"
(charm/help-view (:help state))))
Most components accept style options:
(charm/spinner :dots
:style (charm/style :fg charm/cyan))
(charm/text-input :prompt "Name: "
:prompt-style (charm/style :fg charm/green :bold true)
:text-style (charm/style :fg charm/white)
:cursor-style (charm/style :reverse true))
(charm/item-list items
:cursor-style (charm/style :fg charm/yellow :bold true)
:item-style (charm/style :fg 240))
See styling for full styling documentation.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |