https://github.com/lagunoff/htmlt
Lightweight frontend library for GHC with JavaScript Backend
https://github.com/lagunoff/htmlt
ghc ghcjs gui haskell react virtual-dom
Last synced: 11 months ago
JSON representation
Lightweight frontend library for GHC with JavaScript Backend
- Host: GitHub
- URL: https://github.com/lagunoff/htmlt
- Owner: lagunoff
- Created: 2019-09-19T17:58:58.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-04-13T23:53:21.000Z (almost 2 years ago)
- Last Synced: 2024-04-14T06:55:36.213Z (almost 2 years ago)
- Topics: ghc, ghcjs, gui, haskell, react, virtual-dom
- Language: Haskell
- Homepage:
- Size: 3.54 MB
- Stars: 18
- Watchers: 6
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Lightweight frontend library for GHC with JavaScript backend with
focus on minimalism and simplicity
## Getting started
To follow the instructions, you would need to have [nix](https://nixos.org/download.html)
installed in your system. Alternatively,
you can choose to install manually [GHC with JavaScript
Backend](https://engineering.iog.io/2023-01-24-javascript-browser-tutorial/),
and `cabal` (comes with every [GHC](https://www.haskell.org/ghc/) installation)
How to build library and the examples:
```sh
# Clone the repository
git clone https://github.com/lagunoff/htmlt.git
cd htmlt
# Enter the nix-shell
nix-shell
# Build examples with cabal
cabal --with-ghc=javascript-unknown-ghcjs-ghc --with-ghc-pkg=javascript-unknown-ghcjs-ghc-pkg build -f examples
```
Once `cabal build` is successful, you can find js executables in
`./dist-newstyle/build/javascript-ghcjs/ghc-9.7.20230527/htmlt-0.1.0.0/x` and run them by opening `index.html` in browser
### Minimal example
```haskell
-- Example featuring element and two buttons. The input value
-- is synchronized with 'DynRef's state and can be modified by either entering a
-- number into the input or by clicking one of the two buttons
app :: Html ()
app = do
-- First create a 'DynRef
counterRef <- newRef @Int 0
div_ do
input_ [type_ "number"] do
-- Show the value inside
dynProp "value" $ JSS.pack . show <$> fromRef counterRef
-- Parse and update the value on each InputEvent
on "input" $ decodeEvent intDecoder $ writeRef counterRef
br_
-- Decrease the value on each click
button_ do
on_ "click" $ modifyRef counterRef pred
text "-"
-- Increase the value on each click
button_ do
on_ "click" $ modifyRef counterRef succ
text "+"
where
intDecoder =
valueDecoder >=> MaybeT . pure . readMaybe . JSS.unpack
main :: IO ()
main =
void $ attachToBody app
```
[Open the demo](https://lagunoff.github.io/htmlt/js-backend/htmlt-counter.jsexe/)
## Quick API summary
Expand to see simplified definitions
```hs
-- Constructing DOM
el :: JSString -> Html a -> Html a
elns :: JSString -> JSString -> Html a -> Html a
text :: JSString -> Html ()
dynText :: Dynamic JSString -> Html ()
-- Applying attributes and properties
prop :: JSString -> v -> Html ()
dynProp :: JSString -> Dynamic v -> Html ()
attr :: JSString -> JSString -> Html ()
dynAttr :: JSString -> JSString -> Html ()
toggleClass :: JSString -> Dynamic Bool -> Html ()
toggleAttr :: JSString -> Dynamic Bool -> Html ()
dynStyle :: JSString -> Dynamic JSString -> Html ()
dynStyles :: Dynamic JSString -> Html ()
dynValue :: Dynamic JSString -> Html ()
dynClass :: Dynamic JSString -> Html ()
dynChecked :: Dynamic Bool -> Html ()
dynDisabled :: Dynamic Bool -> Html ()
-- Handling DOM events
on :: EventName -> (DOMEvent -> Step ()) -> Html ()
on_ :: EventName -> Step () -> Html ()
onOptions :: EventName -> ListenerOpts -> (DOMEvent -> Step ()) -> Html ()
onGlobalEvent :: ListenerOpts -> DOMNode -> EventName -> (DOMEvent -> Step ()) -> Html ()
-- Decoding data from DOM Events
mouseDeltaDecoder :: JSVal -> MaybeT m MouseDelta
clientXYDecoder :: JSVal -> MaybeT m (Point Int)
offsetXYDecoder :: JSVal -> MaybeT m (Point Int)
pageXYDecoder :: JSVal -> MaybeT m (Point Int)
keyModifiersDecoder :: JSVal -> MaybeT m KeyModifiers
keyCodeDecoder :: JSVal -> MaybeT m Int
keyboardEventDecoder :: JSVal -> MaybeT m KeyboardEvent
valueDecoder :: JSVal -> MaybeT m JSString
checkedDecoder :: JSVal -> MaybeT m Bool
-- DOM extras, useful helpers
unsafeHtml :: MonadIO m => JSString -> HtmlT m ()
portal :: Monad m => DOMElement -> HtmlT m a -> HtmlT m a
installFinalizer :: MonadReactive m => IO () -> m ()
-- Dynamic collections
simpleList :: Dynamic [a] -> (Int -> DynRef a -> Html ()) -> Html ()
-- Arbitrary dynamic content
dyn :: Dynamic (Html ()) -> Html ()
-- Contructing Events
newEvent :: MonadReactive m => m (Event a, Trigger a)
fmap :: (a -> b) -> Event a -> Event a
never :: Event a
updates :: Dynamic a -> Event a
-- Constructing Dynamics
constDyn :: a -> Dynamic a
fromRef :: DynRef a -> Dynamic a
fmap :: (a -> b) -> Dynamic a -> Dynamic b
(<*>) :: Dynamic (a -> b) -> Dynamic a -> Dynamic b
mapDyn :: MonadReactive m => Dynamic a -> (a -> b)-> m (Dynamic b)
mapDyn2 :: MonadReactive m => Dynamic a -> Dynamic b -> (a -> b -> c) -> m (Dynamic c)
mapDyn3 :: MonadReactive m => Dynamic a -> Dynamic b -> Dynamic c -> (a -> b -> c -> d) -> m (Dynamic d)
holdUniqDyn :: Eq a => Dynamic a -> Dynamic a
holdUniqDynBy :: (a -> a -> Bool) -> Dynamic a -> Dynamic a
-- Constructing DynRefs
newRef :: MonadReactive m => a -> m (DynRef a)
lensMap :: Lens' s a -> DynRef s -> DynRef a
-- Read and write DynRefs, Dynamics
readDyn :: MonadIO m => Dynamic a -> m a
readRef :: MonadIO m => DynRef a -> m a
writeRef :: DynRef a -> a -> Step ()
modifyRef :: DynRef a -> (a -> a) -> Step ()
atomicModifyRef :: DynRef a -> (a -> (a, r)) -> Step r
-- Starting and shutting down the application
atatchOptions :: StartOpts -> Html a -> IO (a, RunningApp)
attachTo :: DOMElement -> Html a -> IO (a, RunningApp)
attachToBody :: Html a -> IO (a, RunningApp)
detach :: RunningApp -> IO ()
```
## Other examples
Counter
5.6M all.js, 3.7M all.min.js
source
open |
open minified
TodoMVC
3.1M all.js, 773K all.min.js
source
open |
open minified
Simple Routing
11M all.js, 7.6M all.min.js
source
open |
open minified
For comparison, here are the sizes of all.js files build with GHCJS 8.6
— 1.5M
[htmlt-counter](https://lagunoff.github.io/htmlt-counter/),
1.4M
[htmlt-todomvc](https://lagunoff.github.io/htmlt-todomvc/),
3.3M
[htmlt-simple-routing](https://lagunoff.github.io/htmlt-simple-routing/)
## Todos
- [x] Migrate to GHC with JavaScript backend
- [ ] More examples and documentation
- [ ] Similar library for ReactNative
## Legacy GHCJS version
The legacy version for GHCJS 8.6 and GHCJS 8.10 can still be found in
the [ghcjs](https://github.com/lagunoff/htmlt/tree/ghcjs) branch