From eecc45f28472982ca5e38732fe9adedb293126c2 Mon Sep 17 00:00:00 2001 From: niole Date: Sat, 13 Jan 2018 16:10:03 -0800 Subject: [PATCH] Reset Calendar and DatePicker --- source/Ui/Calendar.elm | 395 ++++++++++++++++++--------------------- source/Ui/DatePicker.elm | 238 ++++++++++------------- 2 files changed, 283 insertions(+), 350 deletions(-) diff --git a/source/Ui/Calendar.elm b/source/Ui/Calendar.elm index 5fca408..3c9264f 100644 --- a/source/Ui/Calendar.elm +++ b/source/Ui/Calendar.elm @@ -1,360 +1,325 @@ -module Ui.Calendar - exposing - ( Model - , Msg - , init - , onChange - , update - , view - , render - , selectable - , setValue - , nextDay - , previousDay - ) +module Ui.Calendar exposing + ( Model, Msg, init, onChange, update, view, render, selectable + , setValue, nextDay, previousDay ) {-| Simple calendar component: - - Change month by clicking on arrows on the left or right in the header - Select a date by clicking on it - Can be rendered in a given locale - # Model - @docs Model, Msg, init, update - # DSL - @docs selectable - # Events - @docs onChange - # View - @docs view, render - # Functions - @docs setValue, nextDay, previousDay - -} import Html exposing (node, text, span) import Html.Events exposing (onClick) import Html.Lazy + import Date.Extra.Config.Configs as DateConfigs import Date.Extra.Format exposing (format) import Time exposing (Time) import Ext.Date import Date + import Ui.Helpers.Emitter as Emitter import Ui.Native.Uid as Uid import Ui.Container import Ui.Icons import Ui + import Ui.Styles.Calendar exposing (defaultStyle) import Ui.Styles - {-| Representation of a calendar component: - - **selectable** - Whether or not the user can select a date by clicking on it - **disabled** - Whether or not the calendar is disabled - **readonly** - Whether or not the calendar is readonly - **uid** - The unique identifier of the calendar - **date** - The month which is displayed - **value** - The current selected date - -} type alias Model = - { selectable : Bool - , value : Date.Date - , date : Date.Date - , disabled : Bool - , readonly : Bool - , uid : String - } + { selectable : Bool + , value : Date.Date + , date : Date.Date + , disabled : Bool + , readonly : Bool + , uid : String + } {-| Messages that a calendar can receive. -} type Msg - = Select Date.Date - | PreviousMonth - | NextMonth + = Select Date.Date + | PreviousMonth + | NextMonth {-| Initializes a calendar. - calendar = - Ui.Calendar.init () - + calendar = Ui.Calendar.init () -} init : () -> Model init _ = - { value = Ext.Date.now () - , date = Ext.Date.now () - , selectable = True - , disabled = False - , readonly = False - , uid = Uid.uid () - } + { value = Ext.Date.now () + , date = Ext.Date.now () + , selectable = True + , disabled = False + , readonly = False + , uid = Uid.uid () + } {-| Sets the selectable property of a calendar. -} selectable : Bool -> Model -> Model selectable value model = - { model | selectable = value } + { model | selectable = value } {-| Subscribe to the changes of a calendar. - subscription = - Ui.Calendar.onChange CalendarChanged calendar - + subscription = Ui.Calendar.onChange CalendarChanged calendar -} onChange : (Time -> msg) -> Model -> Sub msg onChange msg model = - Emitter.listenFloat model.uid msg + Emitter.listenFloat model.uid msg {-| Updates a calendar. - ( updatedCalendar, cmd ) = - Ui.Calendar.update msg calendar - + ( updatedCalendar, cmd ) = Ui.Calendar.update msg calendar -} update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = - case msg of - PreviousMonth -> - ( { model | date = Ext.Date.previousMonth model.date }, Cmd.none ) - - NextMonth -> - ( { model | date = Ext.Date.nextMonth model.date }, Cmd.none ) - - Select date -> - if Ext.Date.isSameDate model.value date then - ( model, Cmd.none ) - else - ( { model | value = date } - , Emitter.sendFloat model.uid (Date.toTime date) - ) + case msg of + PreviousMonth -> + ( { model | date = Ext.Date.previousMonth model.date }, Cmd.none ) + + NextMonth -> + ( { model | date = Ext.Date.nextMonth model.date }, Cmd.none ) + + Select date -> + if Ext.Date.isSameDate model.value date then + ( model, Cmd.none ) + else + ( { model | value = date } + , Emitter.sendFloat model.uid (Date.toTime date) + ) {-| Lazily renders a calendar in the given locale. Ui.Calendar.view "en_us" calendar - -} view : String -> Model -> Html.Html Msg view locale model = - Html.Lazy.lazy2 render locale model + Html.Lazy.lazy2 render locale model {-| Renders a calendar in the given locale. Ui.Calendar.render "en_us" calendar - -} render : String -> Model -> Html.Html Msg render locale model = - let - -- The date of the month - month = - Ext.Date.begginingOfMonth model.date - - -- List of dates in the month - dates = - Ext.Date.datesInMonth month - - -- The left padding in the table - leftPadding = - paddingLeft month - - -- The cells before the month - paddingLeftItems = - Ext.Date.previousMonth month - |> Ext.Date.datesInMonth - |> List.reverse - |> List.take (paddingLeft month) - |> List.reverse - - -- The cells after the month - paddingRightItems = - Ext.Date.nextMonth month - |> Ext.Date.datesInMonth - |> List.take (42 - leftPadding - (List.length dates)) - - -- All of the 42 cells combined - cells = - paddingLeftItems - ++ dates - ++ paddingRightItems - |> List.map (renderCell model) - - nextAction = - Ui.enabledActions model [ onClick NextMonth ] - - previousAction = - Ui.enabledActions model [ onClick PreviousMonth ] - - -- Header container - container = - Ui.Container.view - { compact = True - , align = "stretch" - , direction = "row" - } - [] - [ Ui.Icons.chevronLeft previousAction - , node "div" - [] - [ text (format (DateConfigs.getConfig locale) "%Y - %B" month) ] - , Ui.Icons.chevronRight nextAction - ] - in - node - "ui-calendar" - ([ Ui.attributeList - [ ( "selectable", model.selectable ) - , ( "disabled", model.disabled ) - , ( "readonly", model.readonly ) - ] - , Ui.Styles.apply defaultStyle - ] - |> List.concat - ) - [ container - , node "ui-calendar-header" - [] - (List.map (\item -> span [] [ text item ]) (dayNames locale)) - , node "ui-calendar-table" [] cells - ] + let + -- The date of the month + month = + Ext.Date.begginingOfMonth model.date + + -- List of dates in the month + dates = + Ext.Date.datesInMonth month + + -- The left padding in the table + leftPadding = + paddingLeft month + + -- The cells before the month + paddingLeftItems = + Ext.Date.previousMonth month + |> Ext.Date.datesInMonth + |> List.reverse + |> List.take (paddingLeft month) + |> List.reverse + + -- The cells after the month + paddingRightItems = + Ext.Date.nextMonth month + |> Ext.Date.datesInMonth + |> List.take (42 - leftPadding - (List.length dates)) + + -- All of the 42 cells combined + cells = + paddingLeftItems + ++ dates + ++ paddingRightItems + |> List.map (renderCell model) + + nextAction = + Ui.enabledActions model [ onClick NextMonth ] + + previousAction = + Ui.enabledActions model [ onClick PreviousMonth ] + + -- Header container + container = + Ui.Container.view + { compact = True + , align = "stretch" + , direction = "row" + } + [] + [ Ui.Icons.chevronLeft previousAction + , node "div" [] + [ text (format (DateConfigs.getConfig locale) "%Y - %B" month) ] + , Ui.Icons.chevronRight nextAction + ] + in + node + "ui-calendar" + ( [ Ui.attributeList + [ ( "selectable", model.selectable ) + , ( "disabled", model.disabled ) + , ( "readonly", model.readonly ) + ] + , Ui.Styles.apply defaultStyle + ] + |> List.concat + ) + [ container + , node "ui-calendar-header" [] + (List.map (\item -> span [] [ text item ]) (dayNames locale)) + , node "ui-calendar-table" [] cells + ] {-| Sets the value of a calendar. updatedCalendar = - Ui.Calendar.setValue (Ext.Date.createDate 1977 5 25) calendar - + Ui.Calendar.setValue (Ext.Date.createDate 1977 5 25) calendar -} setValue : Date.Date -> Model -> Model setValue date model = - { model | value = date } - |> fixDate + { model | value = date } + |> fixDate {-| Steps the selected value to the next day. -} nextDay : Model -> Model nextDay model = - { model | value = Ext.Date.nextDay model.value } - |> fixDate + { model | value = Ext.Date.nextDay model.value } + |> fixDate {-| Steps the selected value to the previous day. -} previousDay : Model -> Model previousDay model = - { model | value = Ext.Date.previousDay model.value } - |> fixDate + { model | value = Ext.Date.previousDay model.value } + |> fixDate {-| Fixes the date in order to make sure the selected date is visible. -} fixDate : Model -> Model fixDate model = - if Ext.Date.isSameMonth model.date model.value then - model - else - { model | date = model.value } + if Ext.Date.isSameMonth model.date model.value then + model + else + { model | date = model.value } {-| Returns the padding based on the day of the week. -} paddingLeft : Date.Date -> Int paddingLeft date = - case Date.dayOfWeek date of - Date.Mon -> - 0 + case Date.dayOfWeek date of + Date.Mon -> + 0 - Date.Tue -> - 1 + Date.Tue -> + 1 - Date.Wed -> - 2 + Date.Wed -> + 2 - Date.Thu -> - 3 + Date.Thu -> + 3 - Date.Fri -> - 4 + Date.Fri -> + 4 - Date.Sat -> - 5 + Date.Sat -> + 5 - Date.Sun -> - 6 + Date.Sun -> + 6 {-| Returns the short names of days. -} dayNames : String -> List String dayNames locale = - let - config = - DateConfigs.getConfig locale - |> .i18n - in - [ Date.Mon - , Date.Tue - , Date.Wed - , Date.Thu - , Date.Fri - , Date.Sat - , Date.Sun - ] - |> List.map config.dayShort + let + config = + DateConfigs.getConfig locale + |> .i18n + in + [ Date.Mon + , Date.Tue + , Date.Wed + , Date.Thu + , Date.Fri + , Date.Sat + , Date.Sun + ] + |> List.map config.dayShort {-| Renders a single cell. -} renderCell : Model -> Date.Date -> Html.Html Msg renderCell model date = - let - sameMonth = - Ext.Date.isSameMonth date model.date - - value = - model.selectable && (Ext.Date.isSameDate date model.value) - - click = - if - not model.disabled - && not model.readonly - && model.selectable - && sameMonth - then - [ onClick (Select date) ] - else - [] - - attributes = - Ui.attributeList - [ ( "inactive", not sameMonth ) - , ( "selected", value ) - ] - in - node - "ui-calendar-cell" - (attributes ++ click) - [ text (toString (Date.day date)) ] + let + sameMonth = + Ext.Date.isSameMonth date model.date + + value = + model.selectable && (Ext.Date.isSameDate date model.value) + + click = + if not model.disabled + && not model.readonly + && model.selectable + && sameMonth + then + [ onClick (Select date) ] + else + [] + + attributes = + Ui.attributeList + [ ( "inactive", not sameMonth ) + , ( "selected", value ) + ] + in + node + "ui-calendar-cell" + (attributes ++ click) + [ text (toString (Date.day date)) ] diff --git a/source/Ui/DatePicker.elm b/source/Ui/DatePicker.elm index 68d3932..610226a 100644 --- a/source/Ui/DatePicker.elm +++ b/source/Ui/DatePicker.elm @@ -1,66 +1,46 @@ -module Ui.DatePicker - exposing - ( Model - , Msg - , init - , update - , subscriptions - , onChange - , view - , render - , setValue - , closeOnSelect - ) +module Ui.DatePicker exposing + ( Model, Msg, init, update, subscriptions, onChange, view, render, setValue + , closeOnSelect ) {-| An input component that displays a **Calendar** (in a dropdown) when focused, allowing the user to manipulate the selected date. - # Model - @docs Model, Msg, init, subscriptions, update - # DSL - @docs closeOnSelect - # Events - @docs onChange - # View - @docs view, render -@docs setValue - - # Functions - +@docs setValue -} import Html.Events.Extra exposing (onPreventDefault) import Html exposing (node, text) import Html.Lazy + import Date.Extra.Format exposing (isoDateFormat, format) import Date.Extra.Config.Configs as DateConfigs import Time import Date + import Ui.Helpers.Dropdown as Dropdown exposing (Dropdown) import Ui.Helpers.Picker as Picker import Ui.Native.Uid as Uid import Ui.Calendar import Ui.Icons import Ui + import Ui.Styles.DatePicker exposing (defaultStyle) import Ui.Styles - {-| Representation of a date picker: - - **closeOnSelect** - Whether or not to close the dropdown after selecting - **format** - The format of the date to render in the input - **readonly** - Whether or not the date picker is readonly @@ -68,177 +48,165 @@ import Ui.Styles - **uid** - The unique identifier of the date picker - **calendar** - The model of the calendar - **dropdown** - The model of the dropdown - -} type alias Model = - { calendar : Ui.Calendar.Model - , closeOnSelect : Bool - , dropdown : Dropdown - , format : String - , disabled : Bool - , readonly : Bool - , uid : String - } + { calendar : Ui.Calendar.Model + , closeOnSelect : Bool + , dropdown : Dropdown + , format : String + , disabled : Bool + , readonly : Bool + , uid : String + } {-| Messages that a date picker can receive. -} type Msg - = Calendar Ui.Calendar.Msg - | Picker Picker.Msg - | Select Time.Time - | Increment - | Decrement - | NoOp + = Calendar Ui.Calendar.Msg + | Picker Picker.Msg + | Select Time.Time + | Increment + | Decrement + | NoOp {-| Initializes a date picker with the given date. datePicker = - Ui.DatePicker.init () - |> Ui.DatePicker.closeOnSelect true - + Ui.DatePicker.init () + |> Ui.DatePicker.closeOnSelect true -} init : () -> Model init _ = - { calendar = Ui.Calendar.init () - , dropdown = Dropdown.init - , format = isoDateFormat - , closeOnSelect = False - , disabled = False - , readonly = False - , uid = Uid.uid () - } - |> Dropdown.offset 5 + { calendar = Ui.Calendar.init () + , dropdown = Dropdown.init + , format = isoDateFormat + , closeOnSelect = False + , disabled = False + , readonly = False + , uid = Uid.uid () + } + |> Dropdown.offset 5 {-| Subscribe to the changes of a date picker. - subscriptions = - Ui.DatePicker.onChange DatePickerChanged datePicker - + subscriptions = Ui.DatePicker.onChange DatePickerChanged datePicker -} onChange : (Time.Time -> msg) -> Model -> Sub msg onChange msg model = - Ui.Calendar.onChange msg model.calendar + Ui.Calendar.onChange msg model.calendar {-| Subscriptions for a date picker. - subscriptions = - Sub.map DatePicker (Ui.DatePicker.subscriptions datePicker) - + subscriptions = Sub.map DatePicker (Ui.DatePicker.subscriptions datePicker) -} subscriptions : Model -> Sub Msg subscriptions model = - Sub.batch - [ Ui.Calendar.onChange Select model.calendar - , Sub.map Picker (Picker.subscriptions model) - ] + Sub.batch + [ Ui.Calendar.onChange Select model.calendar + , Sub.map Picker (Picker.subscriptions model) + ] {-| Sets whether or not to close the dropdown when selecting an other date. -} closeOnSelect : Bool -> Model -> Model closeOnSelect value model = - { model | closeOnSelect = value } + { model | closeOnSelect = value } {-| Updates a date picker. - ( updatedDatePicker, cmd ) = - Ui.DatePicker.update msg datePicker - + ( updatedDatePicker, cmd ) = Ui.DatePicker.update msg datePicker -} update : Msg -> Model -> ( Model, Cmd Msg ) update action model = - case action of - NoOp -> - ( model, Cmd.none ) - - Calendar act -> - let - ( calendar, effect ) = - Ui.Calendar.update act model.calendar - in - ( { model | calendar = calendar }, Cmd.map Calendar effect ) - - Select time -> - let - updatedModel = - if model.closeOnSelect then - Dropdown.close model - else - model - in - ( updatedModel, Cmd.none ) - - Picker act -> - ( Picker.update act model, Cmd.none ) - - Decrement -> - ( { model | calendar = Ui.Calendar.previousDay model.calendar } - |> Dropdown.open - , Cmd.none - ) - - Increment -> - ( { model | calendar = Ui.Calendar.nextDay model.calendar } - |> Dropdown.open - , Cmd.none - ) + case action of + NoOp -> + ( model, Cmd.none ) + + Calendar act -> + let + ( calendar, effect ) = + Ui.Calendar.update act model.calendar + in + ( { model | calendar = calendar }, Cmd.map Calendar effect ) + + Select time -> + let + updatedModel = + if model.closeOnSelect then + Dropdown.close model + else + model + in + ( updatedModel, Cmd.none ) + + Picker act -> + ( Picker.update act model, Cmd.none ) + + Decrement -> + ( { model | calendar = Ui.Calendar.previousDay model.calendar } + |> Dropdown.open + , Cmd.none + ) + + Increment -> + ( { model | calendar = Ui.Calendar.nextDay model.calendar } + |> Dropdown.open + , Cmd.none + ) {-| Lazily renders a date picker in the given locale. Ui.DatePicker.view "en_us" model - -} view : String -> Model -> Html.Html Msg view locale model = - Html.Lazy.lazy2 render locale model + Html.Lazy.lazy2 render locale model {-| Renders a date picker in the given locale. Ui.DatePicker.render "en_us" model - -} render : String -> Model -> Html.Html Msg render locale model = - let - dateText = - (format (DateConfigs.getConfig locale) model.format model.calendar.value) - in - Picker.view - { attributes = Ui.Styles.apply defaultStyle - , address = Picker - , keyActions = - [ ( 40, Increment ) - , ( 38, Decrement ) - , ( 39, Increment ) - , ( 37, Decrement ) - ] - , contents = - [ node "ui-date-picker-content" [] [ text dateText ] - , Ui.Icons.calendar [] - ] - , dropdownContents = - [ node "ui-date-picker-calendar" - [ onPreventDefault "mousedown" NoOp ] - [ Html.map Calendar (Ui.Calendar.view locale model.calendar) - ] - ] - } - model + let + dateText = + (format (DateConfigs.getConfig locale) model.format model.calendar.value) + in + Picker.view + { attributes = Ui.Styles.apply defaultStyle + , address = Picker + , keyActions = + [ ( 40, Increment ) + , ( 38, Decrement ) + , ( 39, Increment ) + , ( 37, Decrement ) + ] + , contents = + [ node "ui-date-picker-content" [] [ text dateText ] + , Ui.Icons.calendar [] + ] + , dropdownContents = + [ node "ui-date-picker-calendar" + [ onPreventDefault "mousedown" NoOp ] + [ Html.map Calendar (Ui.Calendar.view locale model.calendar) + ] + ] + } model {-| Sets the value of a date picker ( updatedDatePicker, cmd ) = - Ui.DatePicker.setValue (Ext.Date.create 1980 5 17) datePicker - + Ui.DatePicker.setValue (Ext.Date.create 1980 5 17) datePicker -} setValue : Date.Date -> Model -> Model setValue date model = - { model | calendar = Ui.Calendar.setValue date model.calendar } + { model | calendar = Ui.Calendar.setValue date model.calendar }