emojinput/elm/Main.elm

283 lines
6.9 KiB
Elm
Raw Normal View History

2022-02-05 21:05:41 +01:00
port module Main exposing (..)
import Browser
import Element exposing (Element)
import Element.Border as Border
2022-02-19 16:39:05 +01:00
import Element.Font as Font
2022-02-05 21:05:41 +01:00
import Element.Input as Input
import Emoji
import Html
import Html.Attributes
import Html.Events
import Json.Decode as Decode
2022-02-19 13:59:43 +01:00
import Levenshtein exposing (distance)
2022-02-05 21:05:41 +01:00
main =
Browser.element
{ init = init
, update = update
, subscriptions = \_ -> Sub.none
, view = view
}
-- MODEL --
type Model
= Search String
| Category Emoji.Category
modelCategory : Model -> Maybe Emoji.Category
modelCategory model =
case model of
Category c ->
Just c
_ ->
Nothing
modelSearch : Model -> Maybe String
modelSearch model =
case model of
Search s ->
Just s
_ ->
Nothing
modelEmojis : Model -> List Emoji.Emoji
modelEmojis model =
case model of
Category c ->
Emoji.getEmojis c
Search s ->
Emoji.categories
2022-02-19 13:59:43 +01:00
|> List.map Emoji.getEmojis
2022-02-25 11:35:38 +01:00
|> List.map (List.filter (\x -> String.contains s x.name || List.any (String.contains s) x.tags))
2022-02-19 13:59:43 +01:00
|> doubleMap (\x -> ( x, distance s x.name ))
|> List.map (List.sortBy Tuple.second)
|> mergeBy Tuple.second
2022-02-05 21:05:41 +01:00
|> List.map Tuple.first
init : () -> ( Model, Cmd Msg )
init _ =
2022-02-19 13:59:43 +01:00
( Category Emoji.Smileys, Cmd.none )
2022-02-05 21:05:41 +01:00
type Msg
= CategoryClicked Emoji.Category
| SearchChanged String
| EnterPressed
| Copy String
-- UPDATE --
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
CategoryClicked category ->
( Category category, Cmd.none )
SearchChanged search ->
( Search search, Cmd.none )
EnterPressed ->
( model
, List.head (modelEmojis model)
|> Maybe.map .unicode
|> Maybe.map copy
|> Maybe.withDefault Cmd.none
)
Copy c ->
( model, copy c )
-- VIEW --
view : Model -> Html.Html Msg
view model =
[ header model, panel (modelEmojis model) ]
2022-02-19 16:39:05 +01:00
|> Element.column [ Font.size 25, Element.width (Element.px 1024), Element.height (Element.px 768) ]
2022-02-05 21:05:41 +01:00
|> Element.layout []
header : Model -> Element Msg
header model =
Element.row [ Element.width Element.fill, Element.padding 5, Element.spacing 10 ]
[ Element.row [ Element.width Element.fill, Element.spacing 10 ]
(Emoji.categories
|> List.map
(\x ->
Input.button
[ Element.padding 5
, Border.color (Element.rgb255 0 0 0)
, if modelCategory model == Just x then
Border.width 5
else
Border.width 1
, Border.rounded 5
]
2022-02-19 16:39:05 +01:00
{ label = Element.el [ Element.centerX, Element.centerY ] (Element.text (Emoji.categoryEmoji x).unicode)
2022-02-05 21:05:41 +01:00
, onPress = Just (CategoryClicked x)
}
)
)
, Input.text
[ Element.width Element.fill
, Input.focusedOnLoad
, onEnter EnterPressed
]
{ label = Input.labelHidden "input"
, onChange = SearchChanged
, placeholder = Just (Input.placeholder [] (Element.text "search"))
, text = modelSearch model |> Maybe.withDefault ""
}
]
panel : List Emoji.Emoji -> Element Msg
panel em =
em
2022-02-19 16:39:05 +01:00
|> regroup 20
2022-02-05 21:05:41 +01:00
|> List.map (\x -> List.map maybeEmoji x)
|> List.map (Element.row [ Element.width Element.fill ])
2022-02-19 16:39:05 +01:00
|> Element.column [ Element.width (Element.px 1024), Element.height (Element.px 748), Element.scrollbarY ]
2022-02-05 21:05:41 +01:00
emoji : Emoji.Emoji -> Element Msg
emoji e =
Input.button [ Element.htmlAttribute (Html.Attributes.title e.name), Element.centerX, Element.centerY ]
{ label = Element.text e.unicode, onPress = Just (Copy e.unicode) }
maybeEmoji : Maybe Emoji.Emoji -> Element Msg
maybeEmoji e =
Element.el [ Element.width Element.fill, Element.padding 10 ] (Maybe.map emoji e |> Maybe.withDefault Element.none)
regroup : Int -> List Emoji.Emoji -> List (List (Maybe Emoji.Emoji))
regroup num input =
List.reverse (regroupAux [] [] num input)
regroupAux : List (List (Maybe Emoji.Emoji)) -> List (Maybe Emoji.Emoji) -> Int -> List Emoji.Emoji -> List (List (Maybe Emoji.Emoji))
regroupAux currentTotal currentPart num input =
case input of
[] ->
if List.length currentPart < num then
regroupAux currentTotal (Nothing :: currentPart) num []
else
List.reverse currentPart :: currentTotal
h :: t ->
if List.length currentPart >= num then
regroupAux (List.reverse currentPart :: currentTotal) [ Just h ] num t
else
regroupAux currentTotal (Just h :: currentPart) num t
-- PORTS --
port copy : String -> Cmd msg
-- UTILS --
2022-02-21 21:11:31 +01:00
minimum : comparable -> List comparable -> comparable
minimum h t =
2022-02-05 21:05:41 +01:00
case List.minimum t of
Nothing ->
h
Just v ->
min h v
2022-02-21 21:11:31 +01:00
maximum : comparable -> List comparable -> comparable
maximum h t =
2022-02-05 21:05:41 +01:00
case List.maximum t of
Nothing ->
h
Just v ->
max h v
onEnter : msg -> Element.Attribute msg
onEnter msg =
Element.htmlAttribute
(Html.Events.on "keyup"
(Decode.field "key" Decode.string
|> Decode.andThen
(\key ->
if key == "Enter" then
Decode.succeed msg
else
Decode.fail "Not the enter key"
)
)
)
2022-02-19 13:59:43 +01:00
mergeAux : (a -> comparable) -> List a -> List a -> List a
mergeAux comparator listOne listTwo =
case ( listOne, listTwo ) of
( _, [] ) ->
listOne
( [], _ ) ->
listTwo
( frontOne :: restOne, frontTwo :: restTwo ) ->
if comparator frontOne < comparator frontTwo then
frontOne :: mergeAux comparator restOne listTwo
else
frontTwo :: mergeAux comparator listOne restTwo
mergeBy : (a -> comparable) -> List (List a) -> List a
mergeBy comparator input =
case input of
[] ->
[]
[ [] ] ->
[]
[ a ] ->
a
[ a, b ] ->
mergeAux comparator a b
a :: b :: t ->
mergeBy comparator (mergeAux comparator a b :: t)
doubleMap : (a -> b) -> List (List a) -> List (List b)
doubleMap map input =
List.map (\x -> List.map map x) input