packages feed

lucid-htmx (empty) → 0.1.0.0

raw patch · 9 files changed

+460/−0 lines, 9 filesdep +basedep +luciddep +lucid-htmxsetup-changed

Dependencies added: base, lucid, lucid-htmx, servant, text

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Changelog for lucid-htmx++## 0.1.0.0++First release of `lucid-htmx`!
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2021++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# lucid-htmx
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ lucid-htmx.cabal view
@@ -0,0 +1,52 @@+cabal-version:      1.12+name:               lucid-htmx+version:            0.1.0.0+license:            BSD3+license-file:       LICENSE+copyright:          (c) 2021 Wavi Labs LLC+maintainer:         rashad@wavilabs.com+author:             Wavi Labs LLC+homepage:           https://github.com/WaviLabs/lucid-htmx#readme+bug-reports:        https://github.com/WaviLabs/lucid-htmx/issues+synopsis:           Use htmx in your lucid templates+description:+    Please see the README on GitHub at <https://github.com/WaviLabs/lucid-htmx#readme>++category:           Web, HTML+build-type:         Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+    type:     git+    location: https://github.com/WaviLabs/lucid-htmx++library+    exposed-modules:+        Lucid.HTMX+        Lucid.HTMX.QuasiQuoters+        Lucid.HTMX.Servant++    hs-source-dirs:   src+    other-modules:    Paths_lucid_htmx+    default-language: Haskell2010+    build-depends:+        base >=4.7 && <5,+        lucid >=2.9.12.1 && <2.10,+        servant >=0.18.3 && <0.19,+        text >=1.2.4.1 && <1.3++test-suite lucid-htmx-test+    type:             exitcode-stdio-1.0+    main-is:          Spec.hs+    hs-source-dirs:   test+    other-modules:    Paths_lucid_htmx+    default-language: Haskell2010+    ghc-options:      -threaded -rtsopts -with-rtsopts=-N+    build-depends:+        base >=4.7 && <5,+        lucid >=2.9.12.1 && <2.10,+        lucid-htmx -any,+        servant >=0.18.3 && <0.19,+        text >=1.2.4.1 && <1.3
+ src/Lucid/HTMX.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE OverloadedStrings #-}++module Lucid.HTMX where++import Lucid.Base (Attribute, makeAttribute)+import Data.Text (Text)++-- | <https://htmx.org/attributes/hx-boost/>+hxBoost_ :: Text -> Attribute+hxBoost_ = makeAttribute "data-hx-boost"++-- | <https://htmx.org/attributes/hx-confirm/>+hxConfirm_ :: Text -> Attribute+hxConfirm_ = makeAttribute "data-hx-confirm"++-- | <https://htmx.org/attributes/hx-delete/>+hxDelete_ :: Text -> Attribute+hxDelete_ = makeAttribute "data-hx-delete"++-- | <https://htmx.org/attributes/hx-disable/>+hxDisable_ :: Attribute+hxDisable_ = makeAttribute "data-hx-disable" mempty++-- | <https://htmx.org/attributes/hx-encoding/>+hxEncoding_ :: Text -> Attribute+hxEncoding_ = makeAttribute "data-hx-encoding"++-- | <https://htmx.org/attributes/hx-ext/>+hxExt_ :: Text -> Attribute+hxExt_ = makeAttribute "data-hx-ext"++-- | <https://htmx.org/attributes/hx-get/>+hxGet_ :: Text -> Attribute+hxGet_ = makeAttribute "data-hx-get"++-- | <https://htmx.org/attributes/hx-headers/>+hxHeaders_ :: Text -> Attribute+hxHeaders_ = makeAttribute "data-hx-headers"++-- | <https://htmx.org/attributes/hx-history-alt/>+hxHistoryElt_ :: Attribute+hxHistoryElt_ = makeAttribute "data-hx-history-elt" mempty++-- | <https://htmx.org/attributes/hx-include/>+hxInclude_ :: Text -> Attribute+hxInclude_ = makeAttribute "data-hx-include"++-- | <https://htmx.org/attributes/hx-indicator/>+hxIndicator_ :: Text -> Attribute+hxIndicator_ = makeAttribute "data-hx-indicator"++-- | <https://htmx.org/attributes/hx-params/>+hxParams_ :: Text -> Attribute+hxParams_ = makeAttribute "data-hx-params"++-- | <https://htmx.org/attributes/hx-patch/>+hxPatch_ :: Text -> Attribute+hxPatch_ = makeAttribute "data-hx-patch"++-- | <https://htmx.org/attributes/hx-post/>+hxPost_ :: Text -> Attribute+hxPost_ = makeAttribute "data-hx-post"++-- | <https://htmx.org/attributes/hx-preserve/>+hxPreserve_ :: Text -> Attribute+hxPreserve_ = makeAttribute "data-hx-preserve"++-- | <https://htmx.org/attributes/hx-prompt/>+hxPrompt_ :: Text -> Attribute+hxPrompt_ = makeAttribute "data-hx-prompt"++-- | <https://htmx.org/attributes/hx-push-url/>+hxPushUrl_ :: Text -> Attribute+hxPushUrl_ = makeAttribute "data-hx-push-url"++-- | <https://htmx.org/attributes/hx-put/>+hxPut_ :: Text -> Attribute+hxPut_ = makeAttribute "data-hx-put"++-- | <https://htmx.org/attributes/hx-request/>+hxRequest_ :: Text -> Attribute+hxRequest_ = makeAttribute "data-hx-request"++-- | <https://htmx.org/attributes/hx-select/>+hxSelect_ :: Text -> Attribute+hxSelect_ = makeAttribute "data-hx-select"++-- | <https://htmx.org/attributes/hx-sse/>+hxSse_ :: Text -> Attribute+hxSse_ = makeAttribute "data-hx-sse"++-- | <https://htmx.org/attributes/hx-swap-oob/>+hxSwapOob_ :: Text -> Attribute+hxSwapOob_ = makeAttribute "data-hx-swap-oob"++-- | <https://htmx.org/attributes/hx-swap/>+hxSwap_ :: Text -> Attribute+hxSwap_ = makeAttribute "data-hx-swap"++-- | <https://htmx.org/attributes/hx-target/>+hxTarget_ :: Text -> Attribute+hxTarget_ = makeAttribute "data-hx-target"++-- | <https://htmx.org/attributes/hx-trigger/>+hxTrigger_ :: Text -> Attribute+hxTrigger_ = makeAttribute "data-hx-trigger"++-- | <https://htmx.org/attributes/hx-vals/>+hxVals_ :: Text -> Attribute+hxVals_ = makeAttribute "data-hx-vals"++-- | <https://htmx.org/attributes/hx-ws/>+hxWs_ :: Text -> Attribute+hxWs_ = makeAttribute "data-hx-ws"
+ src/Lucid/HTMX/QuasiQuoters.hs view
@@ -0,0 +1,215 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Lucid.HTMX.QuasiQuoters where++{-+{- Idea for QuasiQuoting syntax:++[htmx| hx-get="bar/foo/baz?id=34" hx-swap="delay:500" hx-trigger="click" |]++-}+import Data.Text as Text+import HTMX.Types+import Lucid.HTMX++toUrlPiece' = ("/" <>) . toUrlPiece++-- -- | Makes hx_boost_ a "boolean attribute" since the only valid value for hx-boost is "true".+-- hx_boost_ :: Attribute+-- hx_boost_ = Base.hx_boost_ "true"++-- hx_confirm_ :: Text -> Attribute+-- hx_confirm_ = Base.hx_confirm_++hxDeleteSafe_ :: Link -> Attribute+hxDeleteSafe_ = Base.hx_delete_ . toUrlPiece'++-- hx_disable_ :: Attribute+-- hx_disable_ = Base.hx_disable_++-- | Makes hx_encoding_ a "boolean attribute" since the only valid value for hx-encoding is "multipart/form-data".+-- hx_encoding_ :: Attribute+-- hx_encoding_ = Base.hx_encoding_ "multipart/form-data"++getHTMXExtName :: HTMXExtension -> Text+getHTMXExtName htmxExt = case htmxExt of+    JSONEnc -> "json-enc"+    MethodOverride -> "method-override"+    MorphdomSwap -> "morphdom-swap"+    ClientSideTemplates -> "client-side-templates"+    Debug -> "debug"+    PathDeps -> "path-deps"+    ClassTools -> "class-tools"+    RemoveMe -> "remove-me"+    IncludeVals -> "included-vals"+    AJAXHeader -> "ajax-header"+    EventHeader -> "event-header"+    Preload -> "preload"+    OtherHTMXExtension extName -> extName++hxExtSafe_ :: HXExt -> Attribute+hxExtSafe_ HXExt{..} =+   hxExt_+   . ((if hxExtIgnore then "ignore:" else "") <>)+   . Text.intercalate ","+   . Prelude.map getHTMXExtName+   . HashSet.toList+   $ hxExtExtensions+   -- HXExtVal htmxExtHashSet -> Base.hx_ext_ . Text.intercalate "," . Prelude.map getHTMXExtName . HashSet.toList $ htmxExtHashSet+   -- HXExtValIgnore htmxExtHashSet -> Base.hx_ext_ . ("ignore:" <>) . Text.intercalate "," . Prelude.map getHTMXExtName . HashSet.toList $ htmxExtHashSet++hxGetSafe_ :: Link -> Attribute+hxGetSafe_ = Base.hx_get_ . toUrlPiece'++-- | Value of hx_headers_ must be valid JSON+hxHeadersSafe_ :: HXHeaders -> Attribute+hxHeadersSafe_ HXHeaders{..} =+   Base.hx_headers_+   . Text.decodeUtf8+   . LBS.toStrict+   . Aeson.encode+   $ hxHeadersJSON++-- hx_history_elt_ :: Attribute+-- hx_history_elt_ = Base.hx_history_elt_++hxIncludeSafe_ :: HXInclude -> Attribute+hxIncludeSafe_ HXInclude{..} = Base.hx_include_ . toCssSelector++hxIndicatorSafe_ :: HXIndicator -> Attribute+hxIndicatorSafe_ HXIndicator{..} =+    hxIndicator_+    . ((if hxIndicatorClosest then "closest:" else "") <>)+    . toCssSelector+    $ hxIndicatorSelector++-- hx_indicator_ :: HXIndicatorVal -> Attribute +-- hx_indicator_ val = case val of+--     HXIndicatorVal selector -> Base.hx_indicator_ . toCssSelector $ selector+--     HXIndicatorValClosest selector -> Base.hx_indicator_ . ("closest " <>) . toCssSelector $ selector++hxParamsSafe_ :: HXParams -> Attribute+hxParamsSafe_ hxParams = case hxParams of+    HXParams params -> hxParams_ . Text.intercalate "," $ params+    HXParamsNot params -> hxParams_ . ("not " <>) . Text.intercalate "," $ params+    HXParamsAll -> hxParams_ "*"+    HXParamsNone -> hxParams_ "none"++hx_patch_ :: Link -> Attribute+hx_patch_ = Base.hx_patch_ . toUrlPiece'++hx_post_ :: Link -> Attribute+hx_post_ = Base.hx_post_ . toUrlPiece'++-- For same reasons as hx_boost_+hx_preserve_ :: Attribute+hx_preserve_ = Base.hx_preserve_ "true"++hx_prompt_ :: Text -> Attribute+hx_prompt_ = Base.hx_prompt_++hx_push_url_ :: Link -> Attribute+hx_push_url_ = Base.hx_push_url_ . toUrlPiece'++hx_put_ :: Link -> Attribute+hx_put_ = Base.hx_put_ . toUrlPiece'++hx_request_ :: HXRequestVal -> Attribute+hx_request_ val = Base.hx_request_ $ case val of+    (HXRequestVal (JavaScript _) _ _) -> ("javascript:" <>) . Text.decodeUtf8 . LBS.toStrict . Aeson.encode $ val+    (HXRequestVal _ (JavaScript _) _) -> ("javascript:" <>) . Text.decodeUtf8 . LBS.toStrict . Aeson.encode $ val+    (HXRequestVal _ _ (JavaScript _)) -> ("javascript:" <>) . Text.decodeUtf8 . LBS.toStrict . Aeson.encode $ val+    _ -> Text.decodeUtf8 . LBS.toStrict . Aeson.encode $ val++hx_select_ :: ToCssSelector a => a -> Attribute+hx_select_ = Base.hx_select_ . toCssSelector++hx_sse_ :: HXSSEVal -> Attribute+hx_sse_ val = Base.hx_sse_ $ case val of+    (HXSSEVal Nothing Nothing) -> ""+    (HXSSEVal (Just link) Nothing) -> "connect:" <> (toUrlPiece' link)+    (HXSSEVal Nothing (Just eventName)) -> "swap:" <> eventName+    (HXSSEVal (Just link) (Just eventName)) -> "connect:" <> (toUrlPiece' link) <> " " <> "swap:" <> eventName++pos :: SwapPos -> Text+pos p = case p of+    SwapPosInner -> "innerHTML"+    SwapPosOuter -> "outerHTML"+    SwapPosBeforeBegin -> "beforebegin"+    SwapPosAfterBegin -> "afterbegin"+    SwapPosBeforeEnd -> "beforeend"+    SwapPosAfterEnd -> "afterend"+    SwapPosNone -> "none"++hx_swap_ :: HXSwapVal -> Attribute+hx_swap_ HXSwapVal{..} = Base.hx_swap_ $ (pos hxSwapValPos) <> (swap hxSwapValSwap) <> (settle hxSwapValSettle) <> (view hxSwapValView)+    where+        swap :: Maybe SwapModDelay -> Text+        swap s = case s of+            Nothing -> ""+            Just (SwapModDelay delay) -> " swap:" <> (Text.pack $ show delay) <> "s"++        settle :: Maybe SwapModSettle -> Text+        settle s = case s of+            Nothing -> ""+            Just (SwapModSettle delay) -> " settle:" <> (Text.pack $ show delay) <> "s"++        view :: Maybe SwapModView -> Text+        view v = case v of+            Nothing -> ""+            Just v' -> " " <> (viewPrefix v') <> (viewPostfix v')+            where+                viewPostfix :: SwapModView -> Text+                viewPostfix v' = case v' of+                    SwapModView _ sm ss -> case (sm, ss) of+                        (ScrollMoveTop, ss') -> (selectorPrefix ss') <> "top"+                        (ScrollMoveBottom, ss') -> (selectorPrefix ss') <> "bottom"++                selectorPrefix :: Maybe ScrollSelector -> Text+                selectorPrefix ss = case ss of+                    Nothing -> ""+                    Just ss' -> case ss' of+                        ScrollSelector q -> (toCssSelector q) <> ":"+                        ScrollSelectorWindow -> "window:"++                viewPrefix :: SwapModView -> Text+                viewPrefix v' = case v' of+                      SwapModView SwapModViewTypeScroll _ _ -> "scroll:"+                      SwapModView SwapModViewTypeShow _ _ -> "show:"++hx_swap_oob_ :: HXSwapOOBVal -> Attribute+hx_swap_oob_ val = Base.hx_swap_oob_ $ case val of+    HXSwapOOBVal -> "true"+    HXSwapOOBValSwap swapPos -> pos swapPos+    HXSwapOOBValSwapSelector swapPos selector -> (pos swapPos) <> ":" <> (toCssSelector selector)++hx_target_ :: HXTargetVal -> Attribute+hx_target_ val = Base.hx_target_ $ case val of+    HXTargetVal -> "this"+    HXTargetValSelector selector -> toCssSelector selector+    HXTargetValSelectorClosest selector -> "closest " <> (toCssSelector selector)+    HXTargetValSelectorFind selector -> "find " <> (toCssSelector selector) ++hx_vals_ :: ToJSON a => a -> Attribute+hx_vals_ = Base.hx_vals_ . Text.decodeUtf8 . LBS.toStrict . Aeson.encode++-- TODO: Study more. Basically all possible events plus event modifiers.++-- hx_trigger_ :: HXTriggerVal -> Attribute+-- hx_trigger_ = Base.hx_trigger_++-- BELOW EXPERIMENTAL!!++-- hx_ws_ :: HXWS -> Attribute+-- hx_ws_ = hx_ws_++-- TODO: Add QuasiQuoters for parsing and generating values that are checked at compile time for the various arguments to the HTMX attributes.+-- TODO: Write tests to check that the Val types are generating the correct Text for the HTMX attributes. Tests for HTMX tag functionality maybe?++-}
+ src/Lucid/HTMX/Servant.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE OverloadedStrings #-}++module Lucid.HTMX.Servant where++import Data.Text+import Lucid.Base (Attribute)+import Lucid.HTMX+    ( hxGet_+    , hxPost_+    , hxPut_+    , hxPatch_+    , hxDelete_+    , hxPushUrl_+    )+import Servant.Links (Link)+import Servant.API (toUrlPiece, ToHttpApiData(..))++toUrl :: ToHttpApiData a => a -> Text+toUrl = ("/" <>) . toUrlPiece++hxGetSafe_ :: Link -> Attribute+hxGetSafe_ = hxGet_ . toUrl++hxPostSafe_ :: Link -> Attribute+hxPostSafe_ = hxPost_ . toUrl++hxPutSafe_ :: Link -> Attribute+hxPutSafe_ = hxPut_ . toUrl++hxPatchSafe_ :: Link -> Attribute+hxPatchSafe_ = hxPatch_ . toUrl++hxDeleteSafe_ :: Link -> Attribute+hxDeleteSafe_ = hxDelete_ . toUrl++hxPushUrlSafe_ :: Either Bool Link -> Attribute+hxPushUrlSafe_ boolOrUrl = hxPushUrl_ $ case boolOrUrl of+    Left bool -> if bool then "true" else "false"+    Right url -> toUrl url
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"