Core concepts
Pursx
Or how I learned to stop worrying and copy-paste the DOM.
Sometimes, you procure a quality snippet of HTML that you would like to throw in a Deku project with minimal hassle. For example, imagine that I get the following chunk of HTML from a designer.
<div class="bg-white">
<div class="mx-auto max-w-7xl py-12 px-4 text-center sm:px-6 lg:py-16 lg:px-8">
<h2 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
<span class="block">Ready to dive in?</span>
<span class="block">Start your free trial today.</span>
</h2>
<div class="mt-8 flex justify-center">
<div class="inline-flex rounded-md shadow">
<button class="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-5 py-3 text-base font-medium text-white hover:bg-indigo-700">Get started</button>
</div>
<div class="ml-3 inline-flex">
<button class="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-100 px-5 py-3 text-base font-medium text-indigo-700 hover:bg-indigo-200">Learn more</button>
</div>
</div>
</div>
</div>
This renders in the DOM like so.
Ready to dive in? Start your free trial today.
I could meticulously rewrite the entire thing in Deku, at which point my designer would complain to our boss:
This guy is so obsessed with using their pet functional programming language that they spent two hours rewriting HTML snippets in some esoteric format that none of us understand. Can't we just hire a JavaScript developer?
After having been fired from several companies for this exact reason, I developed Pursx, which solves this and many other problems. In this section, you too will learn how to use Pursx (and, by extension, how to keep your job).
A simple example
Let's start with a simple example. The HTML will be a bit on the long side, but the Deku will be short and sweet! As a motivating example, we’ll be developing breadcrumbs.
<nav class="flex" aria-label="Breadcrumb">
<ol role="list" class="flex space-x-4 rounded-md bg-white px-6 shadow">
<li class="flex">
<div class="flex items-center">
<span class="cursor-pointer text-gray-400 hover:text-gray-500">
<!-- Heroicon name: mini/home -->
<svg class="h-5 w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
<path fill-rule="evenodd" fill="#7393B3" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
</svg>
<span class="sr-only cursor-pointer">Home</span>
</span>
</div>
</li>
<li class="flex">
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">Projects</span>
</div>
</li>
<li class="flex">
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">Project Nero</span>
</div>
</li>
</ol>
</nav>
This renders in the DOM like so.
By the end of this page, we’ll have our breadcrumbs hooked up to stateful logic. We'll start by seeing how to render the example above in Deku.
Plain old HTML
Here is the Deku code that produces the breadcrumb example above.
VITE_START=PlainOldHtml pnpm example
module Examples.PlainOldHtml where
import Deku.Toplevel (runInBody)
import Effect (Effect)
import Prelude
import ExampleAssitant (ExampleSignature)
import Deku.Toplevel (runInBody)
import Deku.Pursx (pursx)
import Deku.Toplevel (runInBody)
type MyHtml =
"""<nav class="flex" aria-label="Breadcrumb">
<ol role="list" class="flex space-x-4 rounded-md bg-white px-6 shadow">
<li class="flex">
<div class="flex items-center">
<span class="cursor-pointer text-gray-400 hover:text-gray-500">
<!-- Heroicon name: mini/home -->
<svg class="h-5 w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
<path fill-rule="evenodd" fill="#7393B3" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
</svg>
<span class="sr-only cursor-pointer">Home</span>
</span>
</div>
</li>
<li class="flex">
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">Projects</span>
</div>
</li>
<li class="flex">
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">Project Nero</span>
</div>
</li>
</ol>
</nav>"""
main :: Effect Unit
main = void $ runInBody (pursx @MyHtml {})
Just so that you don't miss it, after the large block of HTML, here's the actual Deku bit.
main :: Effect Unit
main = runInBody (myHtml ~~ {})
All you have to do is take your HTML string, add ~~ {}
after it, and you get your HTML in the DOM prest-o change-o. In addition to being low-code, this is the most performant Deku gets. It literally takes the code and sets it as the innerHTML
of some containing element.
Type safety
Going back to our HTML string, you’ll see that it's actually not a String
in the “the type of this term is String
” sense of the term. Instead, it's of type Proxy html
, where html
is a Symbol
(aka a type-level String
) containing your HTML. That is, the Proxy
type constructor is parameterized by your HTML, much as Array
can be parameterized by Int
or String
or Nu Maybe
.
By using a type to specify the HTML, Deku can validate it at compile time instead of at runtime. That means you do not need to eat up precious CPU cycles in the browser and you’ll weed out errors earlier. If the HTML isn't valid, your program won't compile.
Let's see a concrete example of this. Say I try to compile the following Deku program with malformed HTML.
main :: Effect Unit
main = runInBody ((Proxy :: Proxy
"<h1><span>hi<span></h1>") ~~ {})
The compiler complains with the following message.
Could not match type
"h1"
with type
"span"
While the message could be better by identifying line numbers (we’re working on this…), it identifies the HTML tag mismatch that needs to be corrected. Once we correct it, the program compiles!
Closing tags and tree structure
The Pursx parser gets better all the time, but it does have some limitations. Even though <br>
is perfectly valid HTML5, the Pursx parser does not recognize it yet. Instead, you will have to write <br />
. Tools like Dreamweaver emit trailing slashes as a default, but you may need to run your HTML through a formatter if these slashes are not present.
At the moment, Pursx needs to be in a single enclosing element. If you have multiple elements, make sure to wrap them in a div
or another suitable container. In the future, we may ease this requirement in order to support a list of elements.
Dynamic attributes
Let's bring our Pursx to live by adding some dynamic attributes. We'll modify the breadcrumbs example above with a hook that sets the presence or absence of a crumb based on a click listener.
Adding an attribute
First, let's add a single listener that sets the breadcrumbs' visibiltiy based on interactions with an anchor tag. Here's the code.
VITE_START=AddingAnAttributeToPursx pnpm example
module Examples.AddingAnAttributeToPursx where
import Deku.Toplevel (runInBody)
import Effect (Effect)
import Data.Foldable (oneOf)
import Prelude
import ExampleAssitant (ExampleSignature)
import Deku.Toplevel (runInBody)
import Data.Tuple.Nested ((/\))
import Deku.DOM.Attributes as DA
import Deku.Toplevel (runInBody)
import Deku.Control (text_)
import Deku.DOM as D
import Deku.Do as Deku
import Deku.Hooks (useState)
import Deku.DOM.Listeners as DL
import Deku.Pursx (pursx)
import Deku.Toplevel (runInBody)
type MyHtml =
"""<nav class="flex" aria-label="Breadcrumb">
<ol role="list" class="flex space-x-4 rounded-md bg-white px-6 shadow">
<li class="flex">
<div class="flex items-center">
<span class="cursor-pointer text-gray-400 hover:text-gray-500">
<!-- Heroicon name: mini/home -->
<svg class="h-5 w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
<path fill-rule="evenodd" fill="#7393B3" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
</svg>
<span class="sr-only cursor-pointer">Home</span>
</span>
</div>
</li>
<li ~projectsHidden~>
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">Projects</span>
</div>
</li>
<li ~neroHidden~>
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">Project Nero</span>
</div>
</li>
</ol>
</nav>"""
main :: Effect Unit
main = void $ runInBody Deku.do
setProjects /\ projects <- useState true
setNero /\ nero <- useState true
let
hideOnFalse e =
DA.klass $ e <#> (if _ then "" else "hidden ") >>>
(_ <> "flex")
point = DA.klass_ "cursor-pointer mr-4"
toggleHome =
[ point
, DL.click_ \_ -> (setProjects false *> setNero false)
]
toggleProjects =
[ point
, DL.click_ \_ -> (setProjects true *> setNero false)
]
toggleNero =
[ point
, DL.click_ \_ -> (setProjects true *> setNero true)
]
D.div_
[ D.div_
[ D.a toggleHome [ text_ "Go home" ]
, D.a toggleProjects [ text_ "Go to projects" ]
, D.a toggleNero [ text_ "Go to nero" ]
]
, D.div_
[ pursx @MyHtml
{ projectsHidden: oneOf [ hideOnFalse projects ]
, neroHidden: oneOf [ hideOnFalse nero ]
}
]
]
To specify an attribute in Pursx, we pick an identifier for the attribute and enclose it in tildes. For example, the attribute for the Project Nero list element is ~neroHidden~
. Then, when creating the Pursx, we add a field to the record with the name of that identifier followed by whatever attribute we wish to add. In this case, we’re adding a class that hides or shows the breadcrumb.
Adding several attributes
A more natural way to implement the breadcrumbs would be to embed the click listener directly in the crumbs in addition to the anchor elements. We can do this by adding multiple attributes to an element in Pursx.
VITE_START=AddingSeveralAttributesToPursx pnpm example
module Examples.AddingSeveralAttributesToPursx where
import Deku.Toplevel (runInBody)
import Prelude
import Data.Foldable (oneOf)
import ExampleAssitant (ExampleSignature)
import Web.PointerEvent.PointerEvent (PointerEvent)
import Deku.Toplevel (runInBody)
import Data.Tuple.Nested ((/\))
import Deku.Attribute (Attribute)
import Deku.DOM.Attributes as DA
import Deku.Toplevel (runInBody)
import Deku.Control (text_)
import Deku.DOM as D
import Deku.Do as Deku
import FRP.Poll (Poll)
import Deku.Hooks (useState)
import Deku.DOM.Listeners as DL
import Effect (Effect)
import Deku.Pursx (pursx)
import Deku.Toplevel (runInBody)
type MyHtml =
"""<nav class="flex" aria-label="Breadcrumb">
<ol role="list" class="flex space-x-4 rounded-md bg-white px-6 shadow">
<li ~homeAtts~>
<div class="flex items-center">
<span class="cursor-pointer text-gray-400 hover:text-gray-500">
<!-- Heroicon name: mini/home -->
<svg class="h-5 w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
<path fill-rule="evenodd" fill="#7393B3" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
</svg>
<span class="sr-only cursor-pointer">Home</span>
</span>
</div>
</li>
<li ~projectsAtts~>
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700">Projects</span>
</div>
</li>
<li ~neroAtts~>
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">Project Nero</span>
</div>
</li>
</ol>
</nav>"""
main :: Effect Unit
main = void $ runInBody Deku.do
setProjects /\ projects <- useState true
setNero /\ nero <- useState true
let
hideOnFalse e =
DA.klass $ e <#> (if _ then "" else "hidden ") >>>
(_ <> "flex")
toggleHome
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleHome = DL.click_ \_ -> (setProjects false *> setNero false)
toggleProjs
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleProjs = DL.click_ \_ -> (setProjects true *> setNero false)
toggleNero
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleNero = DL.click_ \_ -> (setProjects false *> setNero true)
akls = append [ DA.klass_ "cursor-pointer mr-4" ] <<< pure
D.div_
[ D.div_
[ D.a (akls toggleHome) [ text_ "Go home" ]
, D.a (akls toggleProjs) [ text_ "Go to projects" ]
, D.a (akls toggleNero) [ text_ "Go to nero" ]
]
, D.div_
[ pursx @MyHtml
{ homeAtts: oneOf [ toggleHome, DA.klass_ "flex h-12" ]
, projectsAtts: oneOf [ toggleProjs, hideOnFalse projects ]
, neroAtts: oneOf [ toggleNero, hideOnFalse nero ]
}
]
]
By using the tie fighter <|>
, we were able to compose the breadcrumb's class and its click listener together, just like if we were working with attributes in a Deku component. This allows you to mix Pursx code and Deku components.
No let
polymorphism
PureScript lacks let
polymorphism, meaning that effects like toggleHome
and toggleNero
need an explicit polymorphic signature when used for different DOM elements. Otherwise, they would be specialized to their first call site, which in this case is an a
tag. That's why we use explicit signatures for the toggle effects.
Dynamic elements
Now that we've added dynamic attributes, let's add dynamic elements to our Pursx.
Adding a single DOM element as a component
One natural case for adding elements to Pursx is when dealing with components that are easy to group together. For example, we can rewrite our breadcrumbs example to reuse the li
element.
VITE_START=AddingASingleElementToPursx pnpm example
module Examples.AddingASingleElementToPursx where
import Deku.Toplevel (runInBody)
import Prelude
import Data.Foldable (oneOf)
import ExampleAssitant (ExampleSignature)
import Web.PointerEvent.PointerEvent (PointerEvent)
import Deku.Toplevel (runInBody)
import Data.Tuple.Nested ((/\))
import Deku.Attribute (Attribute)
import Deku.DOM.Attributes as DA
import Deku.Toplevel (runInBody)
import Deku.Control (text_)
import Deku.DOM as D
import FRP.Poll (Poll)
import Deku.Do as Deku
import Deku.Hooks (useState)
import Deku.DOM.Listeners as DL
import Deku.Pursx (pursx)
import Effect (Effect)
import Deku.Toplevel (runInBody)
type LiHtml =
"""<li ~atts~>
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">~name~</span>
</div>
</li>"""
type MyHtml =
"""<nav class="flex" aria-label="Breadcrumb">
<ol role="list" class="flex space-x-4 rounded-md bg-white px-6 shadow">
<li ~homeAtts~>
<div class="flex items-center">
<span class="cursor-pointer text-gray-400 hover:text-gray-500">
<!-- Heroicon name: mini/home -->
<svg class="h-5 w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
<path fill-rule="evenodd" fill="#7393B3" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
</svg>
<span class="sr-only cursor-pointer">Home</span>
</span>
</div>
</li>
~projectLi~
~neroLi~
</ol>
</nav>"""
main :: Effect Unit
main = void $ runInBody Deku.do
setProjects /\ projects <- useState true
setNero /\ nero <- useState true
let
hideOnFalse e =
DA.klass $ e <#> (if _ then "" else "hidden ") >>>
(_ <> "flex")
toggleHome
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleHome = DL.click_ \_ -> (setProjects false *> setNero false)
toggleProjs
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleProjs = DL.click_ \_ -> (setProjects true *> setNero false)
toggleNero
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleNero = DL.click_ \_ -> (setProjects false *> setNero true)
D.div_
[ D.div_
[ D.a [ DA.klass_ "cursor-pointer mr-4", toggleHome ]
[ text_ "Go home" ]
, D.a [ DA.klass_ "cursor-pointer mr-4", toggleProjs ]
[ text_ "Go to projects" ]
, D.a [ DA.klass_ "cursor-pointer", toggleNero ]
[ text_ "Go to nero" ]
]
, D.div_
[ pursx @MyHtml
{ homeAtts: oneOf [ toggleHome, DA.klass_ "flex h-12" ]
, projectLi:
pursx @LiHtml
{ atts: oneOf [ toggleProjs, hideOnFalse projects ]
, name: text_ "Projects"
}
, neroLi:
pursx @LiHtml
{ atts: oneOf [ toggleNero, hideOnFalse nero ]
, name: text_ "Project Nero"
}
}
]
]
To add an element dynamically, we use the same method as adding a dynamic attribute. Just place it between tildes wherever you need it!
Adding several DOM elements as a component
To add several elements, one can use fixed
or <>
.
VITE_START=AddingSeveralElementsToPursx pnpm example
module Examples.AddingSeveralElementsToPursx where
import Deku.Toplevel (runInBody)
import Prelude
import Web.PointerEvent.PointerEvent (PointerEvent)
import FRP.Poll (Poll)
import ExampleAssitant (ExampleSignature)
import Data.Foldable (oneOf)
import Data.Tuple.Nested ((/\))
import Deku.DOM.Attributes as DA
import Deku.Attribute (Attribute)
import Deku.Control (text_)
import Deku.Core (fixed)
import Deku.DOM as D
import Deku.Do as Deku
import Deku.Hooks (useState)
import Effect (Effect)
import Deku.DOM.Listeners as DL
import Deku.Pursx (pursx)
import Deku.Toplevel (runInBody)
type LiHtml =
"""<li ~atts~>
<div class="flex items-center">
<svg class="h-full w-6 flex-shrink-0 text-gray-200" viewBox="0 0 24 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="#7393B3" d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<span class="cursor-pointer ml-4 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">~name~</span>
</div>
</li>"""
type MyHtml =
"""<nav class="flex" aria-label="Breadcrumb">
<ol role="list" class="flex space-x-4 rounded-md bg-white px-6 shadow">
<li ~homeAtts~>
<div class="flex items-center">
<span class="cursor-pointer text-gray-400 hover:text-gray-500">
<!-- Heroicon name: mini/home -->
<svg class="h-5 w-5 flex-shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" aria-hidden="true">
<path fill-rule="evenodd" fill="#7393B3" d="M9.293 2.293a1 1 0 011.414 0l7 7A1 1 0 0117 11h-1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-3a1 1 0 00-1-1H9a1 1 0 00-1 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-6H3a1 1 0 01-.707-1.707l7-7z" clip-rule="evenodd" />
</svg>
<span class="sr-only cursor-pointer">Home</span>
</span>
</div>
</li>
~lis~
</ol>
</nav>"""
main :: Effect Unit
main = void $ runInBody Deku.do
setProjects /\ projects <- useState true
setNero /\ nero <- useState true
let
hideOnFalse e =
DA.klass $ e <#> (if _ then "" else "hidden ") >>>
(_ <> "flex")
toggleHome
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleHome = DL.click_ \_ -> (setProjects false *> setNero false)
toggleProjs
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleProjs = DL.click_ \_ -> (setProjects true *> setNero false)
toggleNero
:: forall r
. Poll (Attribute (click :: PointerEvent | r))
toggleNero = DL.click_ \_ -> (setProjects false *> setNero true)
D.div_
[ D.div_
[ D.a [ DA.klass_ "cursor-pointer mr-4", toggleHome ]
[ text_ "Go home" ]
, D.a
[ DA.klass_ "cursor-pointer mr-4", toggleProjs ]
[ text_ "Go to projects" ]
, D.a [ DA.klass_ "cursor-pointer", toggleNero ]
[ text_ "Go to nero" ]
]
, D.div_
[ pursx @MyHtml
{ homeAtts: oneOf [ toggleHome, DA.klass_ "flex h-12" ]
, lis: fixed
[ pursx @LiHtml
{ atts: oneOf
[ toggleProjs
, hideOnFalse projects
]
, name: text_ "Projects"
}
, pursx @LiHtml
{ atts: oneOf
[ toggleNero, hideOnFalse nero ]
, name: text_ "Project Nero"
}
]
}
]
]