# reflex-dom-ace
## Overview
Basic support for using the Ace editor with Reflex.
This package provides a Reflex wrapper around the Ace editor. It is somewhat incomplete and was derived from [code written for hsnippet](https://github.com/mightybyte/hsnippet/blob/64cc17d2bf2bcce219f3ab8e96b7fd6071d5b56b/frontend/src/ACE.hs).
This is also intended to serve as an example of how to structure FFI packages that rely on external JS packages.
## Example Usage with Obelisk
### Add Dependency to Obelisk
#### Hackage
As per the [Obelisk FAQ](https://github.com/obsidiansystems/obelisk/blob/master/FAQ.md#how-do-i-declare-a-new-haskell-dependency), modify `default.nix` to look like:
```
# ...
project ./. ({ pkgs, ... }: {
# ...
overrides = self: super:
{
reflex-dom-ace = self.callHackageDirect {
pkg = "reflex-dom-ace";
ver = "0.3.0.1";
sha256 = "0kbd3kqmsx4115a39a984m62kgc9s96586c5yx80nijman8j0zlw";
} {};
};
# ...
```
Change the `ver` to match the desired version and leave the `sha256`. Then let `ob run` fail with the expected `sha256` and update the value accordingly.
#### GitHub
As per the [Obelisk FAQ](https://github.com/obsidiansystems/obelisk/blob/master/FAQ.md#how-do-i-declare-a-new-haskell-dependency), modify `default.nix` to look like:
```
# ...
project ./. ({ pkgs, ... }: {
# ...
overrides = self: super: let
aceSrc = pkgs.fetchFromGitHub {
owner = "SlimTim10";
repo = "reflex-dom-ace";
rev = "5d086c871892b9ebf7c66ad2d4d4f84023bea9b2";
sha256 = "0vizlf691s1rbkilhf23c79vkb6pns2x4b6b94szn9wd8mjizxyc";
};
in
{
reflex-dom-ace = self.callCabal2nix "reflex-dom-ace" aceSrc {};
};
# ...
```
Change the `rev` to match the latest commit hash and leave the `sha256`. Then let `ob run` fail with the expected `sha256` and update the value accordingly.
### Get Ace Editor
Get the `src-noconflict` package from [ace-builds](https://github.com/ajaxorg/ace-builds) (tested with [package version 06.7.20](https://github.com/ajaxorg/ace-builds/tree/53be42342df216d2d25dc60a12dfcb263c6f0592/src-noconflict)). Move the `src-noconflict` contents to the project directory `static/ace/`.
E.g.,
```
$ cd ~
$ git clone https://github.com/ajaxorg/ace-builds
$ mv ~/ace-builds/src-noconflict ~/my-obelisk-project/static/ace
```
### Editor.hs
```
module Editor
( widget
) where
import Data.Text (Text)
import Control.Monad (void)
import Data.Functor ((<&>))
import qualified Language.Javascript.JSaddle.Types as JS
import qualified Reflex.Dom.Ace as Ace
import qualified Reflex.Dom.Core as R
import Reflex.Dom.Core ((=:))
widget
:: forall t m.
( R.DomBuilder t m
, R.TriggerEvent t m
, JS.MonadJSM (R.Performable m)
, JS.MonadJSM m
, R.PerformEvent t m
, R.PostBuild t m
, R.MonadHold t m
)
=> m (R.Dynamic t Text)
widget = do
let containerId = "editor"
void $ R.elAttr "div" (
"id" =: containerId
) R.blank
(script, _) <- R.elAttr' "script" (
"src" =: "/static/ace/ace.js"
<> "type" =: "text/javascript"
<> "charset" =: "utf-8"
) R.blank
let scriptLoaded = () <$ R.domEvent R.Load script
let loading = R.el "p" $ R.text "Loading editor..." <&> const (R.constDyn "")
dt :: R.Dynamic t (R.Dynamic t Text) <- R.widgetHold loading
$ R.ffor scriptLoaded
$ const $ do
ace <- do
let
cfg = R.def
{ Ace._aceConfigMode = Just "latex"
}
Ace.aceWidget cfg (Ace.AceDynConfig (Just Ace.AceTheme_Clouds)) R.never containerId "" R.never
return $ Ace.aceValue ace
R.holdDyn "" . R.switchDyn $ R.updated <$> dt
```
### Frontend.hs
```
module Frontend where
import Obelisk.Frontend
import Obelisk.Configs
import Obelisk.Route
import Obelisk.Generated.Static
import Reflex.Dom.Core
import Common.Api
import Common.Route
import qualified Editor
frontend :: Frontend (R FrontendRoute)
frontend = Frontend
{ _frontend_head = do
el "title" $ text "Obelisk Minimal Example"
elAttr "link" ("href" =: static @"main.css" <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
, _frontend_body = do
prerender_ blank $ do
editorContents :: Dynamic t Text <- Editor.widget
-- Do something with editorContents
return ()
}
```
## Important Notes
This currently does not work if your app is using reflex-dom's
mainWidgetWithHead or mainWidgetWithCss.