Shpadoinkle-widgets-0.1.0.0: Shpadoinkle/Widgets/Form/Dropdown.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Shpadoinkle.Widgets.Form.Dropdown where
import Control.Compactable
import Data.Aeson
import Data.Text
import GHC.Generics
import Prelude hiding (div)
import Shpadoinkle
import Shpadoinkle.Html hiding (p, s, s', select, select',
selected)
import Shpadoinkle.Keyboard
import Shpadoinkle.Widgets.Types
default (Text)
data Dropdown p a = Dropdown
{ _considered :: ConsideredChoice p a
, _toggle :: Toggle
}
deriving instance (Show (Selected p a), Show (Considered p a), Show a) => Show (Dropdown p a)
deriving instance (Read (Selected p a), Read (Considered p a), Read a, Ord a) => Read (Dropdown p a)
deriving instance (Eq (Selected p a), Eq (Considered p a), Eq a) => Eq (Dropdown p a)
deriving instance (Ord (Selected p a), Ord (Considered p a), Ord a) => Ord (Dropdown p a)
deriving instance Generic (Dropdown p a)
instance (ToJSON a, ToJSON (Selected p a), ToJSON (Considered p a)) => ToJSON (Dropdown p a)
instance (FromJSON a, FromJSON (Selected p a), FromJSON (Considered p a), Ord a) => FromJSON (Dropdown p a)
instance Control (Dropdown 'One) where
type Val (Dropdown 'One) a = Maybe a
hygiene :: Applicative f => (Hygiene -> f Hygiene) -> Dropdown 'One a -> f (Dropdown 'One a)
hygiene f d = (\x -> d {_toggle = Closed x }) <$> f (togHygiene $ _toggle d)
value :: (Applicative f, Ord a) => (Maybe a -> f (Maybe a)) -> Dropdown 'One a -> f (Dropdown 'One a)
value f d = maybe d (select' d) <$> f (selected d)
instance Control (Dropdown 'AtleastOne) where
hygiene f d = (\x -> d {_toggle = Closed x }) <$> f (togHygiene $ _toggle d)
value f d = select' d <$> f (selected d)
instance (Consideration ConsideredChoice p, Ord a)
=> IsToggle (Dropdown p a) where
close p = shrug $ p { _toggle = close (_toggle p) }
toggle p = shrug $ p { _toggle = toggle (_toggle p) }
open p = shrug $ p { _toggle = open (_toggle p) }
newtype Config m = Config
{ _attrs :: forall a. [(Text, Prop m a)] }
defConfig :: Config m
defConfig = Config []
instance (Compactable (ConsideredChoice p)) => Compactable (Dropdown p) where
compact (Dropdown c t) = Dropdown (compact c) t
separate (Dropdown c t) = let (l,r) = separate c in (Dropdown l t, Dropdown r t)
filter p (Dropdown c t) = Dropdown (Control.Compactable.filter p c) t
partition p (Dropdown c t) = let (l, r) = Control.Compactable.partition p c in (Dropdown l t, Dropdown r t)
instance Semigroup (ConsideredChoice p a) => Semigroup (Dropdown p a) where
Dropdown c t <> Dropdown c' t' = Dropdown (c <> c') (t <> t')
instance Monoid (ConsideredChoice p a) => Monoid (Dropdown p a) where
mempty = Dropdown mempty mempty
instance SetLike (ConsideredChoice p) => SetLike (Dropdown p) where
toSet = toSet . _considered
smap f (Dropdown c t) = Dropdown (smap f c) t
valid (Dropdown c _) = valid c
instance Consideration ConsideredChoice p => Selection Dropdown p where
select (Dropdown c t) x = close $ Dropdown (select c x) t
select' (Dropdown c t) x = close $ Dropdown (select' c x) t
unselected = unselected . _considered
selected = selected . _considered
withOptions x xs = Dropdown (x `withOptions` xs) mempty
withOptions' x xs = Dropdown (x `withOptions'` xs) mempty
instance (Consideration ConsideredChoice p, Deselection ConsideredChoice p)
=> Deselection Dropdown p where
noselection xs = Dropdown (noselection xs) mempty
deselect (Dropdown c t) = close $ Dropdown (deselect c) t
instance Consideration ConsideredChoice p => Consideration Dropdown p where
consider x (Dropdown c t) = Dropdown (consider x c) t
consider' x (Dropdown c t) = Dropdown (consider' x c) t
choose (Dropdown c t) = Dropdown (choose c) t
choice (Dropdown c _) = choice c
considered (Dropdown c _) = considered c
shrug (Dropdown c xs) = Dropdown (shrug c) xs
data Theme m p b = Theme
{ _wrapper :: forall a . [Html m a] -> Html m a
, _header :: forall a . Selected p b -> [Html m a]
, _list :: forall a . [Html m a] -> Html m a
, _item :: forall a . b -> Html m a
}
semantic :: Present b => Present (Selected p b) => Dropdown p b -> Theme m p b
semantic Dropdown {..} = Theme
{ _wrapper = div
[ class' [ ("dropdown", True)
, ("ui", True)
, ("active", _toggle == Open) ]
]
, _header = \cs ->
[ div [ class' "text" ] (present cs)
, i' [ class' ["dropdown", "icon"] ]
]
, _list = div
[ class' [ "menu"
, "transition" ]
]
, _item = div [ class' "item" ] . present
}
act :: ( Considered p ~ Maybe
, Consideration ConsideredChoice p
, Consideration Dropdown p
, Ord a)
=> Dropdown p a -> Dropdown p a
act x | _toggle x == Open =
close $ case considered x of
Just _ -> choose x
_ -> x
act x = open x
dropdown ::
( Considered p ~ Maybe
, Consideration Dropdown p
, Consideration ConsideredChoice p
, Ord a
) => (Dropdown p a -> Theme m p a)
-> Config m -> Dropdown p a -> Html m (Dropdown p a)
dropdown toTheme Config {..} x =
let
Theme {..} = toTheme x
in injectProps
([onKeyup $ \case
Enter -> act x
UpArrow -> considerPrev x
DownArrow -> considerNext x
_ -> x
, onClick $ act x
, tabbable
] ++ _attrs) . _wrapper $
(_header $ selected x) ++
[ _list $ (\y -> injectProps
[ onMouseover $ consider' y x
, onFocus $ consider' y x
, onClick $ select' x y
, tabbable
] . _item $ y) <$> toList (unselected x)
]