lucid-alpine-0.1.0.7: src/Lucid/Alpine.hs
{-# LANGUAGE OverloadedStrings #-}
module Lucid.Alpine
( xData_,
xBind_,
xHtml_,
xCloak_,
xEffect_,
xFor_,
xForKey_,
xIf_,
xIgnore_,
xInit_,
xModel_,
xOn_,
xRef_,
xShow_,
xText_,
xTransition_,
useAlpine,
useAlpineVersion,
)
where
import Data.Text (Text, intercalate, pack)
import Lucid (Html, HtmlT, defer_, script_, src_)
import Lucid.Base (Attribute, makeAttribute)
-- | x-data
-- Declare a new Alpine component and its data for a block of HTML
xData_ :: Text -> Attribute
xData_ = makeAttribute "x-data"
{-
<div x-data="{ open: false }">
...
</div>
-}
-- | x-bind
-- Dynamically set HTML attributes on an element
xBind_ ::
-- | Attribute name
Text ->
Text ->
Attribute
xBind_ attr = makeAttribute ("x-bind:" <> attr)
{-
<div x-bind:class="! open ? 'hidden' : ''">
...
</div>
-}
-- | x-on
-- Listen for browser events on an element
xOn_ ::
-- | Event name
Text ->
Text ->
Attribute
xOn_ event = makeAttribute ("x-on:" <> event)
{-
<button x-on:click="open = ! open">
Toggle
</button>
-}
-- | x-text
-- Set the text content of an element
xText_ :: Text -> Attribute
xText_ = makeAttribute "x-text"
{-
<div>
Copyright ©
<span x-text="new Date().getFullYear()"></span>
</div>
-}
-- | x-html
-- Set the inner HTML of an element
xHtml_ :: Text -> Attribute
xHtml_ = makeAttribute "x-html"
{-
<div x-html="(await axios.get('/some/html/partial')).data">
...
</div>
-}
-- | x-model
-- Synchronize a piece of data with an input element
xModel_ ::
-- | List of x-model modifiers
[Text] ->
Text ->
Attribute
xModel_ mods = case mods of
[] -> makeAttribute "x-model"
_ -> makeAttribute ("x-model." <> intercalate "." mods)
{-
<div x-data="{ search: '' }">
<input type="text" x-model="search">
Searching for: <span x-text="search"></span>
</div>
-}
-- | x-show
-- Toggle the visibility of an element
xShow_ :: Text -> Attribute
xShow_ = makeAttribute "x-show"
{-
<div x-show="open">
...
</div>
-}
-- | x-transition
-- Transition an element in and out using CSS transitions
xTransition_ ::
-- | Transition directive
Maybe Text ->
-- | List of x-transition modifiers
[Text] ->
Text ->
Attribute
xTransition_ Nothing [] _ = makeAttribute "x-transition" mempty -- No directive or modifiers
xTransition_ (Just dir) [] attrVal = makeAttribute ("x-transition:" <> dir) attrVal -- Directive with custom transition classes
xTransition_ Nothing mods _ = makeAttribute ("x-transition." <> intercalate "." mods) mempty -- No directive, but with modifiers
xTransition_ (Just dir) mods _ = makeAttribute ("x-transition:" <> dir <> "." <> intercalate "." mods) mempty -- Directive with modifiers
{-
<div x-show="open" x-transition>
...
</div>
-}
-- | x-for
-- Repeat a block of HTML based on a data set
xFor_ :: Text -> Attribute
xFor_ = makeAttribute "x-for"
xForKey_ :: Text -> Attribute
xForKey_ = makeAttribute ":key"
{-
<template x-for="post in posts">
<h2 x-text="post.title"></h2>
</template>
-}
-- | x-if
-- Conditionally add/remove a block of HTML from the page entirely.
xIf_ :: Text -> Attribute
xIf_ = makeAttribute "x-if"
{-
<template x-if="open">
<div>...</div>
</template>
-}
-- | x-init
-- Run code when an element is initialized by Alpine
xInit_ :: Text -> Attribute
xInit_ = makeAttribute "x-init"
{-
<div x-init="date = new Date()"></div>
-}
-- | x-effect
-- Execute a script each time one of its dependancies change
xEffect_ :: Text -> Attribute
xEffect_ = makeAttribute "x-effect"
{-
<div x-effect="console.log('Count is '+count)"></div>
-}
-- | x-ref
-- Reference elements directly by their specified keys using the $refs magic property
xRef_ :: Text -> Attribute
xRef_ = makeAttribute "x-ref"
{-
<input type="text" x-ref="content">
<button x-on:click="navigator.clipboard.writeText($refs.content.value)">
Copy
</button>
-}
-- | x-cloak
-- Hide a block of HTML until after Alpine is finished initializing its contents
xCloak_ :: Attribute
xCloak_ = makeAttribute "x-cloak" mempty
{-
<div x-cloak>
...
</div>
-}
-- | x-ignore
-- Prevent a block of HTML from being initialized by Alpine
xIgnore_ :: Attribute
xIgnore_ = makeAttribute "x-ignore" mempty
{-
<div x-ignore>
...
</div>
-}
-- | Use this value in your @head_@ tag to use Alpine.js in your lucid templates
useAlpine :: Monad m => HtmlT m ()
useAlpine = script_ [defer_ "", src_ alpineSrc] ("" :: Html ())
-- | Choose the version of Alpine.js to use using a 3-tuple representing semantic versioning
useAlpineVersion :: Monad m => (Int, Int, Int) -> HtmlT m ()
useAlpineVersion semVer = script_ [defer_ "", src_ $ alpineSrcWithSemVer semVer] ("" :: Html ())
alpineSrc :: Text
alpineSrc = "https://unpkg.com/alpinejs"
alpineSrcWithSemVer :: (Int, Int, Int) -> Text
alpineSrcWithSemVer (major, minor, patch) =
alpineSrc
<> "@"
<> showT major
<> "."
<> showT minor
<> "."
<> showT patch
showT :: Show a => a -> Text
showT = pack . show