Skip to content

Commit 6a0a997

Browse files
author
Andika Demas Riyandi
committed
fixing bugs on searchbox and make searchbox widget
1 parent d9183e1 commit 6a0a997

File tree

5 files changed

+92
-50
lines changed

5 files changed

+92
-50
lines changed

src-ui.v3/cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
with-compiler: /opt/ghcjs/8.4/bin/ghcjs
22
compiler: ghcjs
3-
3+
--with-compiler: ghc-8.4.4
44
packages: .
55

66
----------------------------------------------------------------------------

src-ui.v3/cabal.project.local

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
package *
3+
ghc-location: ghc
4+
5+
program-locations
6+
ghc-location: ghc

src-ui.v3/matrix-ui.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ executable matrix-ui
2828
, aeson ^>= 1.4.3
2929
, base ^>= 4.9.1.0 || ^>= 4.11.1.0
3030
, containers ^>= 0.5.7.1
31+
--, jsaddle ^>= 0.9.0.0
3132
, ghcjs-base ^>= 0.2.0.0
3233
, lens ^>= 4.17.1
3334
, text ^>= 1.2.2.2

src-ui.v3/src/Main.hs

Lines changed: 68 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import qualified Data.JSString as JSS
2828
import qualified Data.JSString.Text as JSS
2929
import qualified Data.List as List
3030
import qualified Data.Map.Strict as Map
31+
import qualified Data.Maybe as M
3132
import Data.Monoid (Endo (Endo), appEndo)
3233
import Data.Proxy
3334
import qualified Data.Set as Set
@@ -97,7 +98,7 @@ utc2unix :: UTCTime -> Int
9798
utc2unix x = ceiling (realToFrac (utcTimeToPOSIXSeconds x) :: Double)
9899

99100
bodyElement4 :: forall t m . (SupportsServantReflex t m, MonadFix m, MonadIO m, MonadHold t m, PostBuild t m, DomBuilder t m, Adjustable t m, DomBuilderSpace m ~ GhcjsDomSpace) => m ()
100-
bodyElement4 = do
101+
bodyElement4 = mdo
101102
dynLoc <- browserHistoryWith getLocationUri
102103
let dynFrag = decodeFrag . T.pack . uriFragment <$> dynLoc
103104

@@ -139,49 +140,7 @@ bodyElement4 = do
139140
text ")"
140141

141142
-- search box
142-
searchInputE <- elAttr "div" ("class" =: "item search right clearfix") $ do
143-
divClass "text" $ text "Package Search"
144-
sVal0 <- inputElement $ def & inputElementConfig_elementConfig . elementConfig_initialAttributes .~ fold
145-
["class" =: "input-search","placeholder" =: "search..."]
146-
debounce 0.1 $ _inputElement_input sVal0
147-
148-
let
149-
dynPackagesJss = V.toList . fmap (JSS.textToJSString . pkgNToText) <$> dynPackages0
150-
calcMatches pkgs sJss =
151-
if JSS.length sJss < 3
152-
then []
153-
else matches' pkgs sJss
154-
where
155-
matches' p sJ
156-
| JSS.isInfixOf "^" sJ = filter (JSS.isPrefixOf (JSS.dropWhile (=='^') sJ)) p
157-
| JSS.isInfixOf "$" sJ = filter (JSS.isSuffixOf (JSS.dropWhileEnd (=='$') sJ)) p
158-
| otherwise = filter (JSS.isInfixOf sJ) p
159-
exactMatches pkgs' sJss = List.partition (==(JSS.dropWhileEnd (=='$') sJss)) pkgs'
160-
matchesE = calcMatches <$> current dynPackagesJss <@> (JSS.textToJSString <$> searchInputE)
161-
dynMatches <- holdDyn [] matchesE
162-
dynSearch <- holdDyn "" (JSS.textToJSString <$> searchInputE)
163-
let
164-
exactDyn = splitDynPure $ zipDynWith exactMatches dynMatches dynSearch
165-
matchesMapDyn = Map.fromList . fmap (\p -> (JSS.textFromJSString p,())) <$> (snd exactDyn)
166-
exactMapDyn = Map.fromList . fmap (\p -> (JSS.textFromJSString p,())) <$> (fst exactDyn)
167-
--matchesMapDyn <- holdDyn Map.empty matchesMapE
168-
_ <- el "ul" $ do
169-
listWithKey exactMapDyn $ \eId _ ->
170-
el "li" $ elAttr "a" ("href" =: ("#/package/" <> eId)) $ el "strong" $ text eId
171-
listWithKey matchesMapDyn $ \pId _ ->
172-
el "li" $ elAttr "a" ("href" =: ("#/package/" <> pId)) $ do
173-
let
174-
tbFront txt txtS = T.breakOn txtS txt
175-
tbEnd txt txtS = T.breakOnEnd txtS txt
176-
breakText = tbFront pId . JSS.textFromJSString <$> dynSearch
177-
(dynFirstT,dynSndT) = splitDynPure breakText
178-
breakSndText = zipDynWith tbEnd dynSndT (JSS.textFromJSString <$> dynSearch)
179-
(dynMidT,dynEndT) = splitDynPure breakSndText
180-
dynText dynFirstT
181-
el "strong" $ dynText dynMidT
182-
dynText dynEndT
183-
184-
143+
_ <- searchBoxWidget dynPackages0
185144
el "hr" blank
186145

187146
_ <- dyn $ dynFrag >>= \case
@@ -427,7 +386,7 @@ bodyElement4 = do
427386
addTag0 <- elClass "form" "form" $ do
428387
el "p" $ text "Tag : "
429388
tagName <- textInput iCfg
430-
tagButton <- button_ "Add Tag"
389+
tagButton <- clickElement_ "button" "Add Tag"
431390
let tVal = _textInput_value tagName
432391
evAdd = (tagPromptlyDyn tVal tagButton)
433392
addTagN <- holdDyn "" evAdd
@@ -777,11 +736,72 @@ pkgTagList m = Map.fromListWith (List.++) $ do
777736
joinE :: forall t m a. (Reflex t, MonadHold t m) => Event t (Event t a) -> m (Event t a)
778737
joinE = fmap switch . hold never
779738

780-
button_ :: forall t m. (DomBuilder t m, PostBuild t m) => T.Text -> m (Event t ())
781-
button_ t = do
739+
clickElement_ :: forall t m. (DomBuilder t m, PostBuild t m) => T.Text -> T.Text -> m (Event t ())
740+
clickElement_ elm t = do
782741
let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
783742
& elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
784-
(e, _) <- element "button" cfg $ text t
743+
(e, _) <- element elm cfg $ text t
785744
pure $ domEvent Click e
786745

746+
stripSearch :: JSS.JSString -> JSS.JSString
747+
stripSearch sJ
748+
| Just sJ' <- JSS.stripPrefix "^" sJ = sJ'
749+
| Just sJ' <- JSS.stripSuffix "$" sJ = sJ'
750+
| otherwise = sJ
751+
752+
filterBySearchBox :: [JSS.JSString] -> JSS.JSString -> [JSS.JSString]
753+
filterBySearchBox pkgs sJ
754+
| JSS.isInfixOf "^" sJ = filter (JSS.isPrefixOf (stripSearch sJ)) pkgs
755+
| JSS.isInfixOf "$" sJ = filter (JSS.isSuffixOf (stripSearch sJ)) pkgs
756+
| otherwise = filter (JSS.isInfixOf (stripSearch sJ))pkgs
757+
758+
splitInfixPkg :: JSS.JSString -> JSS.JSString -> (T.Text, T.Text, T.Text)
759+
splitInfixPkg stripSJ pkg = (frontT, midT, backT)
760+
where
761+
textS = JSS.textFromJSString stripSJ
762+
(frontT, reminderT) = T.breakOn textS (JSS.textFromJSString pkg)
763+
(midT, backT) = T.breakOnEnd textS reminderT
764+
765+
calcMatchesNew :: [JSS.JSString] -> JSS.JSString -> Matches
766+
calcMatchesNew pkgs sJss =
767+
if JSS.length sJss < 3
768+
then matchesEmpty
769+
else Matches { matchesInput = textS, matchesExact = exactMap, matchesInfix = othersMap}
770+
where
771+
textS = JSS.textFromJSString sJss
772+
stripSJ = stripSearch sJss
773+
(exactPkg, infixPkg) = List.partition (== stripSJ) pkgs
774+
exactMap = (Map.fromList . fmap (\p -> (JSS.textFromJSString p,()))) exactPkg
775+
othersMap = (Map.fromList . fmap (\p -> (JSS.textFromJSString p, splitInfixPkg stripSJ p))) (filterBySearchBox infixPkg sJss)
776+
777+
searchBoxWidget :: forall t m. (SupportsServantReflex t m, MonadFix m, MonadIO m, MonadHold t m, PostBuild t m, DomBuilder t m, Adjustable t m, DomBuilderSpace m ~ GhcjsDomSpace)
778+
=> Dynamic t (Vector PkgN)
779+
-> m ()
780+
searchBoxWidget dynPkgs0 = mdo
781+
searchInputE <- elAttr "div" ("class" =: "item search right clearfix") $ do
782+
divClass "text" $ text "Package Search"
783+
sVal0 <- inputElement $ def & inputElementConfig_elementConfig . elementConfig_initialAttributes .~ fold ["class" =: "input-search","placeholder" =: "search..."]
784+
& inputElementConfig_setValue .~ clickPkgE
785+
debounce 0.1 $ leftmost [clickPkgE, (_inputElement_input sVal0)]
786+
let
787+
dynPackagesJss = V.toList . fmap (JSS.textToJSString . pkgNToText) <$> dynPkgs0
788+
matchesE = calcMatchesNew <$> current dynPackagesJss <@> (JSS.textToJSString <$> searchInputE)
789+
matchesDyn <- holdDyn matchesEmpty matchesE
790+
clickPkgE <- searchResultWidget matchesDyn
791+
pure ()
787792

793+
searchResultWidget :: forall t m. (MonadFix m, MonadHold t m, PostBuild t m, DomBuilder t m)
794+
=> Dynamic t Matches
795+
-> m (Event t T.Text)
796+
searchResultWidget mDyn =
797+
el "ul" $ do
798+
exactE <- listViewWithKey (matchesExact <$> mDyn) $ \eId _ -> do
799+
(e, _) <- element "li" def $ elAttr "a" ("href" =: ("#/package/" <> eId)) $ el "strong" $ text eId
800+
pure $ domEvent Click e
801+
otherE <- listViewWithKey (matchesInfix <$> mDyn) $ \pId txt -> do
802+
(e, _) <- element "li" def $ elAttr "a" ("href" =: ("#/package/" <> pId)) $ do
803+
dynText . fmap (^. _1) $ txt
804+
el "strong" $ dynText . fmap (^. _2) $ txt
805+
dynText . fmap (^. _3) $ txt
806+
pure $ domEvent Click e
807+
pure $ "" <$ leftmost [exactE, otherE]

src-ui.v3/src/PkgId.hs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
module PkgId
88
( PkgN(..)
99
, TagN(..)
10+
, Matches(..)
11+
, matchesEmpty
1012
, pkgNFromText
1113

1214
, Ver
@@ -38,6 +40,7 @@ import Data.Aeson (FromJSON (..), FromJSONKey (..),
3840
import qualified Data.Aeson as J
3941
import qualified Data.Aeson.Types as J
4042
import qualified Data.Char as C
43+
import qualified Data.Map as Map
4144
import Data.Text (Text)
4245
import qualified Data.Text as T
4346
import Data.Time.Clock.POSIX (POSIXTime, posixSecondsToUTCTime)
@@ -144,4 +147,16 @@ verToText (Ver x) = T.pack . Ver.showVersion . Ver.makeVersion $ x
144147

145148
----------------------------------------------------------------------------
146149
newtype TagN = TagN { tagNToText :: Text }
147-
deriving (Eq,Ord,FromJSON,ToJSON,ToJSONKey,FromJSONKey,FromHttpApiData,ToHttpApiData)
150+
deriving (Eq,Ord,FromJSON,ToJSON,ToJSONKey,FromJSONKey,FromHttpApiData,ToHttpApiData)
151+
152+
data Matches = Matches
153+
{ matchesInput :: Text
154+
, matchesExact :: Map.Map Text ()
155+
, matchesInfix :: Map.Map Text (Text,Text,Text)
156+
}
157+
deriving (Eq,Ord)
158+
159+
matchesEmpty :: Matches
160+
matchesEmpty = Matches { matchesInput = T.empty :: T.Text, matchesExact = Map.empty, matchesInfix = Map.empty}
161+
162+

0 commit comments

Comments
 (0)