Surfs is a library for creating user interfaces in Slack applications. It aims to make creating Slack surfaces enjoyable.
Slack's Block Kit is great! Writing blocks as hash maps is less great! Writing vectors is better! The goal of Surfs is to be a high quality template library for defining the user interface of a Slack application. It should be:
Surfs renders Slack blocks, elements, and composition objects from vectors. It also supports defining and using custom components (to organize and encapsulate Slack elements).
The heart of Surfs is the render
function.
(require '[thlack.surfs :as surfs])
(surfs/render [:button {:action_id "A123"} "Click Me!"])
({:action_id "A123",
:type :button,
:text {:type :plain_text, :text "Click Me!"}})
(surfs/render [:section {:block_id "B123"}
[:text "Important Text"]]
[:section {:block_id "B456"}
[:markdown "# More Text!"]])
({:block_id "B123",
:type :section,
:text {:type :plain_text, :text "Important Text"}}
{:block_id "B456",
:type :section,
:text {:type :mrkdwn, :text "# More Text!", :verbatim false}})
The results of render are typically attached to a Slack API payload in the blocks
attribute.
(require '[thlack.surfs :as surfs])
(require '[clojure.data.json :as json])
(let [blocks (surfs/render
[:section {:block_id "B123"}
[:text "Important Text"]]
[:section {:block_id "B456"}
[:markdown "# More Text!"]])]
(post-message some-token (json/write-string {:channel "C123"
:blocks blocks})))
Custom components can be defined two different ways:
defc
macro;;; Using a function
(defn greeting
[first-name]
[:text (str "Hello " first-name "!")])
;;; Using defc
(defc greeting
[first-name]
[:text (str "Hello " first-name "!")])
Both types of custom component can be used in the head position of a vector within a group of components.
(render [:section {:block_id "B123"}
[greeting "Brian"]])
Custom components defined via defc
can be called as render functions themselves:
(defc greeting
[first-name]
[:text (str "Hello " first-name "!")])
(greeting "Brian")
Children are one or more nested components - such as :option
elements placed in a :static-select
.
[:static-select {:action_id "A123"}
[:placeholder "Choose a topping"]
[:option {:value "pepperoni"} "Pepperoni"]
[:option {:value "pineapple"} "Pineapple"]]
If a child is encountered as a sequence, it will be flattened. This is useful for things like generating options:
[:static-select {:action_id "A123"}
[:placeholder "Options"]
(map (fn [value label] [:option {:value value} label]) data)]
Or separating items:
[:home
(drop 1 (interleave (repeat [:divider]) [[:section {:block_id "1"}
[:text "One!"]]
[:section {:block_id "2"}
[:text "Two!"]]
[:section {:block_id "3"}
[:text "Three!"]]]))]
Concepts are borrowed from and inspired by the following libraries:
Many thanks to these authors and contributors.
Copyright © 2020 Brian Scaturro
Distributed under the Eclipse Public License (see LICENSE)
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close