remove suggestions feature (#141)

This commit is contained in:
Rok Garbas 2020-07-24 23:01:16 +02:00 committed by GitHub
parent 3bf4907596
commit 44cbea8547
Failed to generate hash of commit
14 changed files with 78 additions and 715 deletions

View file

@ -1 +1 @@
9 10

View file

@ -10,11 +10,6 @@
version = "1.1.3"; version = "1.1.3";
}; };
"ohanhi/keyboard" = {
sha256 = "10sbq8v2kydnc3lkydl367g36q2b0xizxl031xyakrgl4zlh07ic";
version = "2.0.1";
};
"truqu/elm-base64" = { "truqu/elm-base64" = {
sha256 = "12w68b4idbs2vn0gm0lj354pm745jb7n0fj69408mpvh5r1z4m1b"; sha256 = "12w68b4idbs2vn0gm0lj354pm745jb7n0fj69408mpvh5r1z4m1b";
version = "2.0.4"; version = "2.0.4";
@ -35,16 +30,6 @@
version = "1.0.2"; version = "1.0.2";
}; };
"Gizra/elm-debouncer" = {
sha256 = "009yw0rb418ar2a458ilr25m8gxrxsv5nvs3ld3l6sy12v12n0yn";
version = "2.0.0";
};
"Skinney/keyboard-events" = {
sha256 = "10qjlpa4byk78sra071w4ghc7b9p2brnppx7aqyy9cmbrmp5nf86";
version = "2.0.1";
};
"elm/core" = { "elm/core" = {
sha256 = "0gyk7lx3b6vx2jlfbxdsb4xffn0wdvg5yxldq50jr2kk5dzc2prj"; sha256 = "0gyk7lx3b6vx2jlfbxdsb4xffn0wdvg5yxldq50jr2kk5dzc2prj";
version = "1.0.4"; version = "1.0.4";
@ -90,11 +75,6 @@
version = "1.1.0"; version = "1.1.0";
}; };
"elm-community/list-extra" = {
sha256 = "1rvr1c8cfb3dwf3li17l9ziax6d1fshkliasspnw6rviva38lw34";
version = "8.2.4";
};
"elm/time" = { "elm/time" = {
sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1"; sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
version = "1.0.0"; version = "1.0.0";

View file

@ -1,43 +1,39 @@
{ {
"type": "application", "type": "application",
"source-directories": [ "source-directories": [
"src" "src"
], ],
"elm-version": "0.19.1", "elm-version": "0.19.1",
"dependencies": { "dependencies": {
"direct": { "direct": {
"Gizra/elm-debouncer": "2.0.0", "NoRedInk/elm-json-decode-pipeline": "1.0.0",
"NoRedInk/elm-json-decode-pipeline": "1.0.0", "elm/browser": "1.0.2",
"Skinney/keyboard-events": "2.0.1", "elm/core": "1.0.4",
"elm/browser": "1.0.2", "elm/html": "1.0.0",
"elm/core": "1.0.4", "elm/http": "2.0.0",
"elm/html": "1.0.0", "elm/json": "1.1.3",
"elm/http": "2.0.0", "elm/regex": "1.0.0",
"elm/json": "1.1.3", "elm/url": "1.0.0",
"elm/regex": "1.0.0", "hecrj/html-parser": "2.3.4",
"elm/url": "1.0.0", "krisajenkins/remotedata": "6.0.1",
"hecrj/html-parser": "2.3.4", "truqu/elm-base64": "2.0.4"
"krisajenkins/remotedata": "6.0.1",
"ohanhi/keyboard": "2.0.1",
"truqu/elm-base64": "2.0.4"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/parser": "1.1.0",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2",
"elm-community/list-extra": "8.2.4",
"rtfeldman/elm-hex": "1.0.0"
}
}, },
"test-dependencies": { "indirect": {
"direct": { "elm/bytes": "1.0.8",
"elm-explorations/test": "1.2.2" "elm/file": "1.0.5",
}, "elm/parser": "1.1.0",
"indirect": { "elm/time": "1.0.0",
"elm/random": "1.0.0", "elm/virtual-dom": "1.0.2",
"elm/svg": "1.0.1" "rtfeldman/elm-hex": "1.0.0"
}
} }
},
"test-dependencies": {
"direct": {
"elm-explorations/test": "1.2.2"
},
"indirect": {
"elm/random": "1.0.0",
"elm/svg": "1.0.1"
}
}
} }

View file

@ -1,14 +1,12 @@
{ {
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"info": {
"lastModified": 1592376843,
"narHash": "sha256-vKchUGsNMQC9vCmTnpssHMV/+cKXA5QhbEoFm0zua1U="
},
"locked": { "locked": {
"lastModified": 1595175601,
"narHash": "sha256-NMxaD3mdKyp+6nidFkZe7XOzVXtWMpOH1v4KYZxo/Z0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "14fcd947a0150b78d7a37bef56c87fcd57808247", "rev": "5717d9d2f7ca0662291910c52f1d7b95b568fec2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -18,14 +16,12 @@
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"info": {
"lastModified": 1592514044,
"narHash": "sha256-jGQJmq14vh9ASt9d5Z9yBPsrbUgroiNVNhsoBabsw0k="
},
"locked": { "locked": {
"lastModified": 1595619256,
"narHash": "sha256-TOSvHwfQevzE/uTFBWYAt1I/F9F3YRkOV8gUrqUQfkk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e051dab9ff8a58455f6b8375856ac2802c6cd261", "rev": "fd0febffda9af90d6d369ecb4cb9aaf347116bce",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -34,17 +30,15 @@
} }
}, },
"poetry2nix": { "poetry2nix": {
"info": {
"lastModified": 1592121810,
"narHash": "sha256-9cGTKtL7JpllwABxa4iRVU4Qw/YwniLni4RATyONz+s="
},
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1595517229,
"narHash": "sha256-5eMk3+Twb2fDyFX5sm33JYSRaPAvKe9vHV5w/HjbiHs=",
"owner": "nix-community", "owner": "nix-community",
"repo": "poetry2nix", "repo": "poetry2nix",
"rev": "e7c69a288c10e4d97816fdabda5ae3f38e21914e", "rev": "270a0b26b773e566ad59927c51d40a5e9b8ff08d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -61,5 +55,5 @@
} }
}, },
"root": "root", "root": "root",
"version": 5 "version": 7
} }

View file

@ -16,7 +16,6 @@ import shlex
import subprocess import subprocess
import sys import sys
import tqdm # type: ignore import tqdm # type: ignore
import typing
import xml.etree.ElementTree import xml.etree.ElementTree
logger = logging.getLogger("import-channel") logger = logging.getLogger("import-channel")
@ -47,12 +46,6 @@ MAPPING = {
"properties": { "properties": {
"type": {"type": "keyword"}, "type": {"type": "keyword"},
# Package fields # Package fields
"package_suggestions": {
"type": "completion",
"analyzer": "lowercase",
"search_analyzer": "lowercase",
"preserve_position_increments": False,
},
"package_hydra_build": { "package_hydra_build": {
"type": "nested", "type": "nested",
"properties": { "properties": {
@ -96,12 +89,6 @@ MAPPING = {
"package_homepage": {"type": "keyword"}, "package_homepage": {"type": "keyword"},
"package_system": {"type": "keyword"}, "package_system": {"type": "keyword"},
# Options fields # Options fields
"option_suggestions": {
"type": "completion",
"analyzer": "lowercase",
"search_analyzer": "lowercase",
"preserve_position_increments": False,
},
"option_name": {"type": "keyword", "normalizer": "lowercase"}, "option_name": {"type": "keyword", "normalizer": "lowercase"},
"option_name_query": {"type": "keyword", "normalizer": "lowercase"}, "option_name_query": {"type": "keyword", "normalizer": "lowercase"},
"option_description": {"type": "text"}, "option_description": {"type": "text"},
@ -113,28 +100,6 @@ MAPPING = {
} }
def parse_suggestions(text: str) -> typing.List[typing.Dict[str, object]]:
"""Tokenize option_name
Example:
services.nginx.extraConfig
- services.nginx.extraConfig
- services.nginx.
- services.
"""
results: typing.List[typing.Dict[str, object]] = [
{"input": text, "weight": 1000 - (((len(text.split(".")) - 1) * 10))},
]
for i in range(len(text.split(".")) - 1):
result = {
"input": ".".join(text.split(".")[: -(i + 1)]) + ".",
"weight": 1000 - ((len(text.split(".")) - 2 - i) * 10) + 1,
}
results.append(result)
return results
def parse_query(text): def parse_query(text):
"""Tokenize package attr_name """Tokenize package attr_name
@ -371,7 +336,6 @@ def get_packages(evaluation, evaluation_builds):
yield dict( yield dict(
type="package", type="package",
package_suggestions=parse_suggestions(attr_name),
package_hydra=hydra, package_hydra=hydra,
package_attr_name=attr_name, package_attr_name=attr_name,
package_attr_name_query=list(parse_query(attr_name)), package_attr_name_query=list(parse_query(attr_name)),
@ -438,7 +402,6 @@ def get_options(evaluation):
yield dict( yield dict(
type="option", type="option",
option_suggestions=parse_suggestions(name),
option_name=name, option_name=name,
option_name_query=parse_query(name), option_name_query=parse_query(name),
option_description=description, option_description=description,

View file

@ -1,52 +0,0 @@
module Example exposing (fuzzTest, unitTest, viewTest)
import Expect exposing (Expectation)
import Fuzz exposing (Fuzzer, int, list, string)
import Main exposing (..)
import Test exposing (..)
import Test.Html.Query as Query
import Test.Html.Selector exposing (tag, text)
{-| See <https://github.com/elm-community/elm-test>
-}
unitTest : Test
unitTest =
describe "simple unit test"
[ test "Inc adds one" <|
\() ->
update Inc (Model 0 "")
|> Tuple.first
|> .counter
|> Expect.equal 1
]
{-| See <https://github.com/elm-community/elm-test>
-}
fuzzTest : Test
fuzzTest =
describe "simple fuzz test"
[ fuzz int "Inc ALWAYS adds one" <|
\ct ->
update Inc (Model ct "")
|> Tuple.first
|> .counter
|> Expect.equal (ct + 1)
]
{-| see <https://github.com/eeue56/elm-html-test>
-}
viewTest : Test
viewTest =
describe "Testing view function"
[ test "Button has the expected text" <|
\() ->
Model 0 ""
|> view
|> Query.fromHtml
|> Query.findAll [ tag "button" ]
|> Query.first
|> Query.has [ text "+ 1" ]
]

View file

@ -1,42 +1,6 @@
import pytest # type: ignore import pytest # type: ignore
@pytest.mark.parametrize(
"text,expected",
[
(
"services.grafana.analytics.reporting.enable",
[
{"input": "services.grafana.analytics.reporting.enable", "weight": 960},
{"input": "services.grafana.analytics.reporting.", "weight": 971},
{"input": "services.grafana.analytics.", "weight": 981},
{"input": "services.grafana.", "weight": 991},
{"input": "services.", "weight": 1001},
],
),
(
"services.nginx.extraConfig",
[
{"input": "services.nginx.extraConfig", "weight": 980},
{"input": "services.nginx.", "weight": 991},
{"input": "services.", "weight": 1001},
],
),
(
"python37Packages.test1_name-test2",
[
{"input": "python37Packages.test1_name-test2", "weight": 990},
{"input": "python37Packages.", "weight": 1001},
],
),
],
)
def test_parse_suggestions(text, expected):
import import_scripts.channel
assert import_scripts.channel.parse_suggestions(text) == expected
@pytest.mark.parametrize( @pytest.mark.parametrize(
"text,expected", "text,expected",
[ [

View file

@ -240,11 +240,11 @@ update msg model =
|> updateWith Home HomeMsg model |> updateWith Home HomeMsg model
( PackagesMsg subMsg, Packages subModel ) -> ( PackagesMsg subMsg, Packages subModel ) ->
Page.Packages.update model.navKey model.elasticsearch subMsg subModel Page.Packages.update model.navKey subMsg subModel
|> updateWith Packages PackagesMsg model |> updateWith Packages PackagesMsg model
( OptionsMsg subMsg, Options subModel ) -> ( OptionsMsg subMsg, Options subModel ) ->
Page.Options.update model.navKey model.elasticsearch subMsg subModel Page.Options.update model.navKey subMsg subModel
|> updateWith Options OptionsMsg model |> updateWith Options OptionsMsg model
( _, _ ) -> ( _, _ ) ->

View file

@ -26,7 +26,9 @@ type Msg
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
update msg model = update msg model =
( model, Cmd.none ) case msg of
NoOp ->
( model, Cmd.none )
@ -34,5 +36,5 @@ update msg model =
view : Model -> Html Msg view : Model -> Html Msg
view model = view _ =
div [] [ text "Home" ] div [] [ text "Home" ]

View file

@ -17,8 +17,6 @@ import Html
, div , div
, dl , dl
, dt , dt
, li
, p
, pre , pre
, span , span
, table , table
@ -28,7 +26,6 @@ import Html
, th , th
, thead , thead
, tr , tr
, ul
) )
import Html.Attributes import Html.Attributes
exposing exposing
@ -42,7 +39,6 @@ import Html.Events
) )
import Html.Parser import Html.Parser
import Html.Parser.Util import Html.Parser.Util
import Http
import Json.Decode import Json.Decode
import Json.Encode import Json.Encode
import Regex import Regex
@ -94,8 +90,12 @@ type Msg
= SearchMsg (Search.Msg ResultItemSource) = SearchMsg (Search.Msg ResultItemSource)
update : Browser.Navigation.Key -> Search.Options -> Msg -> Model -> ( Model, Cmd Msg ) update :
update navKey options msg model = Browser.Navigation.Key
-> Msg
-> Model
-> ( Model, Cmd Msg )
update navKey msg model =
case msg of case msg of
SearchMsg subMsg -> SearchMsg subMsg ->
let let
@ -103,9 +103,6 @@ update navKey options msg model =
Search.update Search.update
"options" "options"
navKey navKey
"option"
options
decodeResultItemSource
subMsg subMsg
model model
in in
@ -211,9 +208,6 @@ viewResultItemDetails channel item =
asCode value = asCode value =
pre [] [ text value ] pre [] [ text value ]
asLink value =
a [ href value ] [ text value ]
githubUrlPrefix branch = githubUrlPrefix branch =
"https://github.com/NixOS/nixpkgs-channels/blob/" ++ branch ++ "/" "https://github.com/NixOS/nixpkgs-channels/blob/" ++ branch ++ "/"
@ -305,7 +299,7 @@ makeRequest options channel queryRaw from size sort =
[ ( field [ ( field
, Json.Encode.object , Json.Encode.object
[ ( "query", Json.Encode.string query ) [ ( "query", Json.Encode.string query )
, ( "boost", Json.Encode.float boost ) , ( "boost", Json.Encode.float <| boost_base * boost )
, ( "analyzer", Json.Encode.string "whitespace" ) , ( "analyzer", Json.Encode.string "whitespace" )
, ( "fuzziness", Json.Encode.string "1" ) , ( "fuzziness", Json.Encode.string "1" )
, ( "_name" , ( "_name"
@ -332,7 +326,7 @@ makeRequest options channel queryRaw from size sort =
[ ( field [ ( field
, Json.Encode.object , Json.Encode.object
[ ( "query", Json.Encode.string query ) [ ( "query", Json.Encode.string query )
, ( "boost", Json.Encode.float boost ) , ( "boost", Json.Encode.float <| boost_base * boost )
, ( "analyzer", Json.Encode.string "whitespace" ) , ( "analyzer", Json.Encode.string "whitespace" )
, ( "fuzziness", Json.Encode.string "1" ) , ( "fuzziness", Json.Encode.string "1" )
, ( "_name" , ( "_name"

View file

@ -19,7 +19,6 @@ import Html
, dl , dl
, dt , dt
, li , li
, p
, table , table
, tbody , tbody
, td , td
@ -39,7 +38,6 @@ import Html.Events
exposing exposing
( onClick ( onClick
) )
import Http
import Json.Decode import Json.Decode
import Json.Decode.Pipeline import Json.Decode.Pipeline
import Json.Encode import Json.Encode
@ -129,8 +127,12 @@ type Msg
= SearchMsg (Search.Msg ResultItemSource) = SearchMsg (Search.Msg ResultItemSource)
update : Browser.Navigation.Key -> Search.Options -> Msg -> Model -> ( Model, Cmd Msg ) update :
update navKey options msg model = Browser.Navigation.Key
-> Msg
-> Model
-> ( Model, Cmd Msg )
update navKey msg model =
case msg of case msg of
SearchMsg subMsg -> SearchMsg subMsg ->
let let
@ -138,9 +140,6 @@ update navKey options msg model =
Search.update Search.update
"packages" "packages"
navKey navKey
"package"
options
decodeResultItemSource
subMsg subMsg
model model
in in

View file

@ -15,12 +15,9 @@ module Search exposing
, view , view
) )
import Array
import Base64 import Base64
import Browser.Dom import Browser.Dom
import Browser.Navigation import Browser.Navigation
import Debouncer.Messages
import Dict
import Html import Html
exposing exposing
( Html ( Html
@ -44,7 +41,6 @@ import Html
import Html.Attributes import Html.Attributes
exposing exposing
( attribute ( attribute
, autocomplete
, autofocus , autofocus
, class , class
, classList , classList
@ -57,16 +53,13 @@ import Html.Attributes
) )
import Html.Events import Html.Events
exposing exposing
( custom ( onClick
, onClick
, onInput , onInput
, onSubmit , onSubmit
) )
import Http import Http
import Json.Decode import Json.Decode
import Json.Encode import Json.Encode
import Keyboard
import Keyboard.Events
import RemoteData import RemoteData
import Task import Task
import Url.Builder import Url.Builder
@ -75,9 +68,6 @@ import Url.Builder
type alias Model a = type alias Model a =
{ channel : String { channel : String
, query : Maybe String , query : Maybe String
, queryDebounce : Debouncer.Messages.Debouncer (Msg a)
, querySuggest : RemoteData.WebData (SearchResult a)
, querySelectedSuggestion : Maybe String
, result : RemoteData.WebData (SearchResult a) , result : RemoteData.WebData (SearchResult a)
, show : Maybe String , show : Maybe String
, from : Int , from : Int
@ -88,20 +78,6 @@ type alias Model a =
type alias SearchResult a = type alias SearchResult a =
{ hits : ResultHits a { hits : ResultHits a
, suggest : Maybe (SearchSuggest a)
}
type alias SearchSuggest a =
{ query : Maybe (List (SearchSuggestQuery a))
}
type alias SearchSuggestQuery a =
{ text : String
, offset : Int
, length : Int
, options : List (ResultItem a)
} }
@ -161,25 +137,7 @@ init channel query show from size sort model =
|> Maybe.withDefault 15 |> Maybe.withDefault 15
in in
( { channel = Maybe.withDefault defaultChannel channel ( { channel = Maybe.withDefault defaultChannel channel
, queryDebounce =
Debouncer.Messages.manual
|> Debouncer.Messages.settleWhenQuietFor (Just <| Debouncer.Messages.fromSeconds 0.4)
|> Debouncer.Messages.toDebouncer
, query = query , query = query
, querySuggest =
query
|> Maybe.map
(\selected ->
if String.endsWith "." selected then
model
|> Maybe.map .querySuggest
|> Maybe.withDefault RemoteData.NotAsked
else
RemoteData.NotAsked
)
|> Maybe.withDefault RemoteData.NotAsked
, querySelectedSuggestion = Nothing
, result = , result =
model model
|> Maybe.map (\x -> x.result) |> Maybe.map (\x -> x.result)
@ -207,34 +165,19 @@ type Msg a
= NoOp = NoOp
| SortChange String | SortChange String
| ChannelChange String | ChannelChange String
| QueryInputDebounce (Debouncer.Messages.Msg (Msg a))
| QueryInput String | QueryInput String
| QueryInputSuggestionsSubmit
| QueryInputSuggestionsResponse (RemoteData.WebData (SearchResult a))
| QueryInputSubmit | QueryInputSubmit
| QueryResponse (RemoteData.WebData (SearchResult a)) | QueryResponse (RemoteData.WebData (SearchResult a))
| ShowDetails String | ShowDetails String
| SuggestionsMoveDown
| SuggestionsMoveUp
| SuggestionsSelect
| SuggestionsClickSelect String
| SuggestionsClose
update : update :
String String
-> Browser.Navigation.Key -> Browser.Navigation.Key
-> String
-> Options
-> Json.Decode.Decoder a
-> Msg a -> Msg a
-> Model a -> Model a
-> ( Model a, Cmd (Msg a) ) -> ( Model a, Cmd (Msg a) )
update path navKey result_type options decodeResultItemSource msg model = update path navKey msg model =
let
requestQuerySuggestionsTracker =
"query-" ++ result_type ++ "-suggestions"
in
case msg of case msg of
NoOp -> NoOp ->
( model ( model
@ -283,94 +226,8 @@ update path navKey result_type options decodeResultItemSource msg model =
|> Browser.Navigation.pushUrl navKey |> Browser.Navigation.pushUrl navKey
) )
QueryInputDebounce subMsg ->
Debouncer.Messages.update
(update path navKey result_type options decodeResultItemSource)
{ mapMsg = QueryInputDebounce
, getDebouncer = .queryDebounce
, setDebouncer = \debouncer m -> { m | queryDebounce = debouncer }
}
subMsg
model
QueryInput query -> QueryInput query ->
update path ( { model | query = Just query }
navKey
result_type
options
decodeResultItemSource
(QueryInputDebounce (Debouncer.Messages.provideInput QueryInputSuggestionsSubmit))
{ model
| query = Just query
, querySuggest = RemoteData.Loading
, querySelectedSuggestion = Nothing
}
|> Tuple.mapSecond
(\cmd ->
if RemoteData.isLoading model.querySuggest then
Cmd.batch
[ cmd
, Http.cancel requestQuerySuggestionsTracker
]
else
cmd
)
QueryInputSuggestionsSubmit ->
let
body =
Http.jsonBody
(Json.Encode.object
[ ( "from", Json.Encode.int 0 )
, ( "size", Json.Encode.int 0 )
, ( "suggest"
, Json.Encode.object
[ ( "query"
, Json.Encode.object
[ ( "text", Json.Encode.string (Maybe.withDefault "" model.query) )
, ( "completion"
, Json.Encode.object
[ ( "field", Json.Encode.string (result_type ++ "_suggestions") )
, ( "skip_duplicates", Json.Encode.bool True )
, ( "size", Json.Encode.int 1000 )
]
)
]
)
]
)
]
)
in
( { model
| querySuggest =
model.query
|> Maybe.map
(\selected ->
if String.endsWith "." selected then
model.querySuggest
else
RemoteData.NotAsked
)
|> Maybe.withDefault RemoteData.NotAsked
, querySelectedSuggestion = Nothing
}
, makeRequest
body
("latest-" ++ String.fromInt options.mappingSchemaVersion ++ "-" ++ model.channel)
decodeResultItemSource
options
QueryInputSuggestionsResponse
(Just requestQuerySuggestionsTracker)
)
QueryInputSuggestionsResponse querySuggest ->
( { model
| querySuggest = querySuggest
, querySelectedSuggestion = Nothing
}
, Cmd.none , Cmd.none
) )
@ -414,155 +271,6 @@ update path navKey result_type options decodeResultItemSource msg model =
|> Browser.Navigation.pushUrl navKey |> Browser.Navigation.pushUrl navKey
) )
SuggestionsMoveDown ->
( { model
| querySelectedSuggestion =
getMovedSuggestion
model.query
model.querySuggest
model.querySelectedSuggestion
(\x -> x + 1)
}
, scrollToSelected "dropdown-menu"
)
SuggestionsMoveUp ->
( { model
| querySelectedSuggestion =
getMovedSuggestion
model.query
model.querySuggest
model.querySelectedSuggestion
(\x -> x - 1)
}
, scrollToSelected "dropdown-menu"
)
SuggestionsSelect ->
case model.querySelectedSuggestion of
Just selected ->
update path
navKey
result_type
options
decodeResultItemSource
(SuggestionsClickSelect selected)
model
Nothing ->
( model
, Task.attempt (\_ -> QueryInputSubmit) (Task.succeed ())
)
SuggestionsClickSelect selected ->
( { model
| querySuggest =
if String.endsWith "." selected then
model.querySuggest
else
RemoteData.NotAsked
, querySelectedSuggestion = Nothing
, query = Just selected
}
, Cmd.batch
[ Task.attempt (\_ -> QueryInputSuggestionsSubmit) (Task.succeed ())
, Task.attempt (\_ -> QueryInputSubmit) (Task.succeed ())
]
)
SuggestionsClose ->
( { model
| querySuggest = RemoteData.NotAsked
, querySelectedSuggestion = Nothing
}
, Cmd.none
)
scrollToSelected :
String
-> Cmd (Msg a)
scrollToSelected id =
let
scroll y =
Browser.Dom.setViewportOf id 0 y
|> Task.onError (\_ -> Task.succeed ())
in
Task.sequence
[ Browser.Dom.getElement (id ++ "-selected")
|> Task.map (\x -> ( x.element.y, x.element.height ))
, Browser.Dom.getElement id
|> Task.map (\x -> ( x.element.y, x.element.height ))
, Browser.Dom.getViewportOf id
|> Task.map (\x -> ( x.viewport.y, x.viewport.height ))
]
|> Task.andThen
(\x ->
case x of
( elementY, elementHeight ) :: ( viewportY, viewportHeight ) :: ( viewportScrollTop, _ ) :: [] ->
let
scrollTop =
scroll (viewportScrollTop + (elementY - viewportY))
scrollBottom =
scroll (viewportScrollTop + (elementY - viewportY) + (elementHeight - viewportHeight))
in
if elementHeight > viewportHeight then
scrollTop
else if elementY < viewportY then
scrollTop
else if elementY + elementHeight > viewportY + viewportHeight then
scrollBottom
else
Task.succeed ()
_ ->
Task.succeed ()
)
|> Task.attempt (\_ -> NoOp)
getMovedSuggestion :
Maybe String
-> RemoteData.WebData (SearchResult a)
-> Maybe String
-> (Int -> Int)
-> Maybe String
getMovedSuggestion query querySuggest querySelectedSuggestion moveIndex =
let
suggestions =
getSuggestions query querySuggest
|> List.filterMap .text
getIndex key =
suggestions
|> List.indexedMap (\i a -> ( a, i ))
|> Dict.fromList
|> Dict.get key
|> Maybe.map moveIndex
|> Maybe.map
(\x ->
if x < 0 then
x + List.length suggestions
else
x
)
getKey index =
suggestions
|> Array.fromList
|> Array.get index
in
querySelectedSuggestion
|> Maybe.andThen getIndex
|> Maybe.withDefault 0
|> getKey
createUrl : createUrl :
String String
@ -736,48 +444,6 @@ fromSortId id =
Nothing Nothing
getSuggestions :
Maybe String
-> RemoteData.WebData (SearchResult a)
-> List (ResultItem a)
getSuggestions query querySuggest =
let
maybeList f x =
x
|> Maybe.map f
|> Maybe.withDefault []
in
case querySuggest of
RemoteData.Success result ->
let
suggestions =
result.suggest
|> maybeList (\x -> x.query |> maybeList (List.map .options))
|> List.concat
|> List.filter
(\x ->
if String.endsWith "." (Maybe.withDefault "" query) then
x.text /= query
else
True
)
firstItemText items =
items
|> List.head
|> Maybe.andThen .text
in
if List.length suggestions == 1 && firstItemText suggestions == query then
[]
else
suggestions
_ ->
[]
view : view :
String String
-> String -> String
@ -786,48 +452,10 @@ view :
-> (Msg a -> b) -> (Msg a -> b)
-> Html b -> Html b
view path title model viewSuccess outMsg = view path title model viewSuccess outMsg =
let
suggestions =
getSuggestions model.query model.querySuggest
viewSuggestion x =
li
[]
[ a
([ href "#" ]
|> List.append
(x.text
|> Maybe.map (\text -> [ onClick <| outMsg (SuggestionsClickSelect text) ])
|> Maybe.withDefault []
)
|> List.append
(if x.text == model.querySelectedSuggestion then
[ id "dropdown-menu-selected" ]
else
[]
)
)
[ text <| Maybe.withDefault "" x.text ]
]
in
div div
[ classList [ class "search-page"
[ ( "search-page", True )
, ( "with-suggestions", RemoteData.isSuccess model.querySuggest && List.length suggestions > 0 )
, ( "with-suggestions-loading"
, (model.query /= Nothing)
&& (model.query /= Just "")
&& not (RemoteData.isSuccess model.querySuggest || RemoteData.isNotAsked model.querySuggest)
)
]
] ]
[ h1 [ class "page-header" ] [ text title ] [ h1 [ class "page-header" ] [ text title ]
, div
[ class "search-backdrop"
, onClick <| outMsg SuggestionsClose
]
[]
, div , div
[ class "search-input" [ class "search-input"
] ]
@ -863,60 +491,19 @@ view path title model viewSuccess outMsg =
[ class "input-append" [ class "input-append"
] ]
[ input [ input
([ type_ "text" [ type_ "text"
, id "search-query-input" , id "search-query-input"
, autocomplete False , autofocus True
, autofocus True , placeholder <| "Search for " ++ path
, placeholder <| "Search for " ++ path , onInput (\x -> outMsg (QueryInput x))
, onInput (\x -> outMsg (QueryInput x)) , value <| Maybe.withDefault "" model.query
, value <| Maybe.withDefault "" model.query ]
]
|> List.append
(if RemoteData.isSuccess model.querySuggest && List.length suggestions > 0 then
[ Keyboard.Events.custom Keyboard.Events.Keydown
{ preventDefault = True
, stopPropagation = True
}
([ ( Keyboard.ArrowDown, SuggestionsMoveDown )
, ( Keyboard.ArrowUp, SuggestionsMoveUp )
, ( Keyboard.Tab, SuggestionsMoveDown )
, ( Keyboard.Enter, SuggestionsSelect )
, ( Keyboard.Escape, SuggestionsClose )
]
|> List.map (\( k, m ) -> ( k, outMsg m ))
)
]
else if RemoteData.isNotAsked model.querySuggest then
[ Keyboard.Events.custom Keyboard.Events.Keydown
{ preventDefault = True
, stopPropagation = True
}
([ ( Keyboard.ArrowDown, QueryInputSuggestionsSubmit )
, ( Keyboard.ArrowUp, QueryInputSuggestionsSubmit )
]
|> List.map (\( k, m ) -> ( k, outMsg m ))
)
]
else
[]
)
)
[] []
, div [ class "loader" ] [] , div [ class "loader" ] []
, div [ class "btn-group" ] , div [ class "btn-group" ]
[ button [ class "btn" ] [ text "Search" ] [ button [ class "btn" ] [ text "Search" ]
] ]
] ]
, ul
[ id "dropdown-menu", class "dropdown-menu" ]
(if RemoteData.isSuccess model.querySuggest && List.length suggestions > 0 then
List.map viewSuggestion suggestions
else
[]
)
] ]
] ]
, case model.result of , case model.result of
@ -1299,24 +886,8 @@ decodeResult :
Json.Decode.Decoder a Json.Decode.Decoder a
-> Json.Decode.Decoder (SearchResult a) -> Json.Decode.Decoder (SearchResult a)
decodeResult decodeResultItemSource = decodeResult decodeResultItemSource =
Json.Decode.map2 SearchResult Json.Decode.map SearchResult
(Json.Decode.field "hits" (decodeResultHits decodeResultItemSource)) (Json.Decode.field "hits" (decodeResultHits decodeResultItemSource))
(Json.Decode.maybe (Json.Decode.field "suggest" (decodeSuggest decodeResultItemSource)))
decodeSuggest : Json.Decode.Decoder a -> Json.Decode.Decoder (SearchSuggest a)
decodeSuggest decodeResultItemSource =
Json.Decode.map SearchSuggest
(Json.Decode.maybe (Json.Decode.field "query" (Json.Decode.list (decodeSuggestQuery decodeResultItemSource))))
decodeSuggestQuery : Json.Decode.Decoder a -> Json.Decode.Decoder (SearchSuggestQuery a)
decodeSuggestQuery decodeResultItemSource =
Json.Decode.map4 SearchSuggestQuery
(Json.Decode.field "text" Json.Decode.string)
(Json.Decode.field "offset" Json.Decode.int)
(Json.Decode.field "length" Json.Decode.int)
(Json.Decode.field "options" (Json.Decode.list (decodeResultItem decodeResultItemSource)))
decodeResultHits : Json.Decode.Decoder a -> Json.Decode.Decoder (ResultHits a) decodeResultHits : Json.Decode.Decoder a -> Json.Decode.Decoder (ResultHits a)

View file

@ -29,56 +29,8 @@ header .navbar.navbar-static-top {
} }
} }
.search-page.with-suggestions-loading {
.input-append div.loader {
display: block;
}
}
.search-page.with-suggestions {
ul.dropdown-menu {
display: block;
}
.input-append input {
border-radius: 4px 0 0 0;
}
.search-backdrop {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 999;
}
}
.search-input { .search-input {
position: relative;
ul.dropdown-menu {
font-size: 18px;
width: 25.6em;
height: auto;
max-height: 200px;
border-radius: 0;
margin-top: -10px;
border-top: 0;
overflow-y: scroll;
li > a {
font-size: 14px;
}
li > a#dropdown-menu-selected {
color: #fff;
text-decoration: none;
background-color: #0081c2;
background-image: -moz-linear-gradient(top,#08c,#0077b3);
background-image: -webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));
background-image: -webkit-linear-gradient(top,#08c,#0077b3);
background-image: -o-linear-gradient(top,#08c,#0077b3);
background-image: linear-gradient(to bottom,#08c,#0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0077b3',GradientType=0);
}
}
.input-append { .input-append {
position: relative;
input { input {
font-size: 18px; font-size: 18px;
height: 40px; height: 40px;

Binary file not shown.