page :: (Hyperbole :> es) => Eff es (Page '[Message])
page = do
pure $ col id $ do
hyper Message1 $ messageView "Hello"
hyper Message2 $ messageView "World!"
data Message = Message1 | Message2
deriving (Show, Read, ViewId)
instance HyperView Message es where
data Action Message = Louder Text
deriving (Show, Read, ViewAction)
update (Louder msg) = do
let new = msg <> "!"
pure $ messageView new
messageView :: Text -> View Message ()
messageView msg = do
row id $ do
button (Louder msg) id "Louder"
el_ $ text msg
Live Examples and Documentation
Hackage documentation is greatly improved, with a step-by-step introduction explaining basics and best practices.
https://docs.hyperbole.live is now available with live examples, including links to source code. Notable additions include:
Interesting, I enjoyed reading through the examples. I had a similar, less developed version of this for a client back in 2020 that had the elm architecture (ish, it was a series of mealy machines with a bell on top) on the server, sending events via web socket to a React.render() as the vdom. It worked fairly well.
Question, how would you handle something like a details element, which can be expanded/collapsed on the browser? A naive interpretation of your framework is that if the view updates due to some other event, the details’ open/close status would be lost due to the vdom dropping the “open” attribute, thereby losing the user’s state.
Cool! Yeah there are so many different ways to implement something like this. After rewriting this multiple times, I’m really happy with the current version, and the tradeoffs it makes.
You can pass view state to actions. If you only had an open / closed state, it’s as simple as just having an Open and Close action, like in the transitions example: Hyperbole Examples
If you had some other unrelated action, you could make it a product type and add the current state to it as a selector.
So, this is contrived, but something like
data Action MyItem = Open | Close | SaveChanges Bool
Where the Bool is the open state. Then write a view function that expects the open state
itemView :: Bool -> View MyItem ()
In update you can call itemView with the current state:
update Open = pure $ itemView True
update Close = pure $ itemView False
update (SaveChanges isOpen) = do
someEffect
pure $ itemView isOpen
very nice design ! great work !
while may I know how this would handle recursive data type.
Data Ticket = FullPrice Float
| DiscountBy Ticket Float
In HTML , can such type to be exposed to a form input based on Hyperbole ? Like ,user pick a drop down with option “Full Price” and “Discount”. if “Discount” is being selected, then a future HTML input form of will be shown up ?
Did you mean to allow for nested discounts? Your type would allow DiscountBy (DiscountBy (DiscountBy (FullPrice 100) 5) 5) 5. I think it might be better represented as:
type Price = Float
type Discount = Float
data Ticket = FullPrice Price | Discounted Discount Price
I don’t fully understand your question, but dropdowns are pretty flexible. You must enumerate the options, and specify the value for each. From https://docs.hyperbole.live/filter
The first parameter is (value -> Action id), and will be called with whichever option is selected. The second parameter is value -> Bool, and whichever option returns true will be selected by default.
If you wanted to show a different form if discount is selected, you could branch based on the currently selected value, displaying different views.