Samples / Controls / sap.m

Single Planning Calendar with Multiple Date Selection

SinglePlanningCalendar with multiple date selection functionality.

Class
z2ui5_cl_smpc_app_109
Repository
abap2UI5/samples-controls
Ports
sap.m.sample.SinglePlanningCalendarDateSelection
UI5 libraries
sap.m, sap.ui.core, sap.ui.unified
Lowest UI5 release
1.123
In the browser
Runs in the playground
Controls it builds
sap.m.Label, sap.m.OverflowToolbar, sap.m.OverflowToolbarLayoutData, sap.m.SinglePlanningCalendar, sap.m.SinglePlanningCalendarDayView, sap.m.SinglePlanningCalendarMonthView, sap.m.SinglePlanningCalendarWeekView, sap.m.SinglePlanningCalendarWorkWeekView, sap.m.ToggleButton, sap.m.ToolbarSeparator, sap.m.VBox, sap.ui.core.mvc.View, sap.ui.unified.CalendarAppointment

The class

" @keywords singleplanningcalendar single planning calendar sap.m day selection vbox overflowtoolbar toolbarseparator label togglebutton
" @summary SinglePlanningCalendar with multiple date selection functionality.
" @origin sap.m.sample.SinglePlanningCalendarDateSelection - https://sdk.openui5.org/entity/sap.m.SinglePlanningCalendar/sample/sap.m.sample.SinglePlanningCalendarDateSelection (status: reviewed - read against the original, not run)
CLASS z2ui5_cl_smpc_app_109 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    " onPress flips the calendar's dateSelectionMode and swaps the button's
    " tooltip - both are bindable properties, so they are held here and bound
    " two-way rather than driven through a frontend action
    DATA date_selection_mode TYPE string.
    DATA multiselect_tooltip TYPE string.

    TYPES:
      BEGIN OF ty_s_appointment,
        title     TYPE string,
        text      TYPE string,
        type      TYPE string,
        icon      TYPE string,
        startdate TYPE string,
        enddate   TYPE string,
      END OF ty_s_appointment.
    DATA t_appointments TYPE STANDARD TABLE OF ty_s_appointment WITH EMPTY KEY.
    DATA startdate TYPE string.

  PROTECTED SECTION.
    " one entry per DateRange the frontend marshalled out of the event's
    " selectedDates parameter - startDate arrives as an ISO LOCAL timestamp
    " (no Z), so its first ten characters are the day the user picked
    TYPES:
      BEGIN OF ty_s_event_range,
        startdate TYPE string,
      END OF ty_s_event_range.
    TYPES ty_t_event_range TYPE STANDARD TABLE OF ty_s_event_range WITH EMPTY KEY.

    DATA client TYPE REF TO z2ui5_if_client.

    METHODS view_display.
    METHODS on_event.
    METHODS event_ranges
      IMPORTING
        val           TYPE string
      RETURNING
        VALUE(result) TYPE ty_t_event_range.
    METHODS model_init.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smpc_app_109 IMPLEMENTATION.

  METHOD z2ui5_if_app~main.

    me->client = client.
    IF client->check_on_init( ).
      model_init( ).
      view_display( ).
    ELSEIF client->check_on_navigated( ).
      view_display( ).
    ELSEIF client->check_on_event( ).
      on_event( ).
    ENDIF.

  ENDMETHOD.


  METHOD view_display.

    DATA(view) = z2ui5_cl_ui5_view_builder=>factory( ).

    " startDate + CalendarAppointment startDate/endDate are object-typed: the model
    " keeps ISO strings and Formatter.DateCreateObject converts them at the binding
    view->ele( n = `View` ns = `mvc`
        )->a( n = `xmlns:mvc`    v = `sap.ui.core.mvc`
        )->a( n = `xmlns:core`   v = `sap.ui.core`
        )->a( n = `xmlns:u`      v = `sap.ui.unified`
        )->a( n = `xmlns`        v = `sap.m`
        )->a( n = `core:require` v = `{Formatter: 'z2ui5/model/formatter'}`

        )->ele( `VBox`
            )->a( n = `class` v = `sapUiSmallMargin`

            )->ele( `OverflowToolbar`
                )->a( n = `height` v = `100%`
                )->a( n = `width`  v = `100%`

                )->tag( `ToolbarSeparator`
                )->tag( `Label`
                    )->a( n = `text` v = `Day selection mode : `
                )->ele( `ToggleButton`
                    )->a( n = `id`      v = `MultiSelect`
                    )->a( n = `icon`    v = `sap-icon://select-appointments`
                    )->a( n = `tooltip` v = client->_bind( multiselect_tooltip )
                    )->a( n = `press`   v = client->_event( `PRESS` )

                    )->ele( `layoutData`
                        )->tag( `OverflowToolbarLayoutData`
                            )->a( n = `priority` v = `NeverOverflow`

                    )->end(
                )->end(
            )->end(

            )->ele( `SinglePlanningCalendar`
                )->a( n = `id`                  v = `SPC1`
                )->a( n = `class`               v = `sapUiSmallMarginTop`
                )->a( n = `title`               v = `My Calendar`
                )->a( n = `dateSelectionMode`   v = client->_bind( date_selection_mode )
                )->a( n = `viewChange`          v = client->_event( `VIEW_CHANGE` )
                " the WHOLE selectedDates parameter travels in one arg: the
                " frontend marshals each DateRange into its public properties
                " (Lib.normalizeEventArgs), which is the loop the client
                " expression grammar does not have
                )->a( n = `selectedDatesChange` v = client->_event( val = `SELECTED_DATE` arg = `${$parameters>/selectedDates}` )
                )->a( n = `weekNumberPress`     v = client->_event( val = `WEEK` arg = `${$parameters>/weekNumber}` )
                )->a( n = `startDateChange`     v = client->_event( val = `START_DATE` arg = `${$parameters>/date}` )
                )->a( n = `startDate`           v = |\{ path: '{ client->_bind_path( startdate ) }', formatter: 'Formatter.DateCreateObject' \}|
                )->a( n = `appointments`        v = client->_bind( t_appointments )

                )->ele( `views`
                    )->tag( `SinglePlanningCalendarDayView`
                        )->a( n = `key`   v = `DayView`
                        )->a( n = `title` v = `Day`
                    )->tag( `SinglePlanningCalendarWorkWeekView`
                        )->a( n = `key`   v = `WorkWeekView`
                        )->a( n = `title` v = `Work Week`
                    )->tag( `SinglePlanningCalendarWeekView`
                        )->a( n = `key`   v = `WeekView`
                        )->a( n = `title` v = `Week`
                    )->tag( `SinglePlanningCalendarMonthView`
                        )->a( n = `key`   v = `MonthView`
                        )->a( n = `title` v = `Month`

                )->end(
                )->ele( `appointments`
                    )->tag( n = `CalendarAppointment` ns = `u`
                        )->a( n = `title`     v = `{TITLE}`
                        )->a( n = `text`      v = `{TEXT}`
                        )->a( n = `type`      v = `{TYPE}`
                        )->a( n = `icon`      v = `{ICON}`
                        )->a( n = `startDate` v = `{ path: 'STARTDATE', formatter: 'Formatter.DateCreateObject' }`
                        )->a( n = `endDate`   v = `{ path: 'ENDDATE', formatter: 'Formatter.DateCreateObject' }` ).

    client->view_display( view->stringify( ) ).

  ENDMETHOD.


  METHOD on_event.

    CASE client->get_event( ).

      WHEN `PRESS`.
        " onPress: SingleSelect <-> MultiSelect, and the tooltip follows the
        " pressed state exactly as the original's setTooltip does
        IF date_selection_mode = `SingleSelect`.
          date_selection_mode = `MultiSelect`.
          multiselect_tooltip = `Disable multi-day selection`.
        ELSE.
          date_selection_mode = `SingleSelect`.
          multiselect_tooltip = `Enable multi-day selection`.
        ENDIF.

      WHEN `VIEW_CHANGE`.
        client->message_toast_display( |'viewChange' event fired.| ).

      WHEN `SELECTED_DATE`.
        " handleSelectedDateChange numbers every selected range and appends its
        " start date, one per line - the array arrives marshalled, so the loop
        " the original writes in JavaScript is an ABAP loop here
        DATA(output) = ``.
        DATA(ranges) = event_ranges( client->get_event_arg( ) ).
        LOOP AT ranges REFERENCE INTO DATA(range).
          IF strlen( range->startdate ) < 10.
            CONTINUE.
          ENDIF.
          output = |{ output }{ sy-tabix }: { range->startdate(10) }\n|.
        ENDLOOP.
        client->message_toast_display( |'selectedDatesChange' event fired.\n\nNew selected dates: \n{ output }| ).

      WHEN `WEEK`.
        " the original appends the pressed week number, which the event carries
        client->message_toast_display( |'weekNumberPress' event fired.\n\nweek number is { client->get_event_arg( ) }| ).

      WHEN `START_DATE`.
        " same for the new start date (the event parameter, not a control ref)
        client->message_toast_display( |'startDateChange' event fired.\n\nNew start date is { client->get_event_arg( ) }| ).

    ENDCASE.

  ENDMETHOD.


  METHOD event_ranges.

    DATA(json) = condense( val ).
    IF json IS INITIAL.
      RETURN.
    ENDIF.

    " a marshalled DateRange carries ALL its public properties (ID,
    " startDate, endDate); this port models exactly one of them.
    "
    " Read by hand: abap2UI5 releases no JSON parser, and the vendored ajson
    " copy is framework-internal (the linter's non-released-api rule reports
    " it, correctly). For ONE property of a flat projection that is the whole
    " job - walk every `"startDate":"` and take what stands up to the next quote.
    " The key is matched in its exact case: the framework writes the UI5
    " property name as it is, and the transpiled runtime's find( ) ignores
    " `case = abap_false` (it matched nothing, so the toast came back empty).
    " The same reader as Z2UI5_CL_SMP_APP_197 in abap2UI5/samples, and the
    " same limit: it reads what the FRAMEWORK wrote, which is flat, and it
    " would need to resolve escapes for a payload composed from free text.
    DATA(marker) = |"startDate":"|.
    DATA(rest)   = json.

    DO.
      DATA(offset) = find( val = rest sub = marker ).
      IF offset < 0.
        EXIT.
      ENDIF.

      rest = substring( val = rest off = offset + strlen( marker ) ).
      INSERT VALUE #( startdate = substring_before( val = rest sub = `"` ) ) INTO TABLE result.
    ENDDO.

  ENDMETHOD.


  METHOD model_init.

    " the calendar opens in single-day selection, and the button offers to
    " enable the multi-day one - the original's view defaults
    date_selection_mode = `SingleSelect`.
    multiselect_tooltip = `Enable multi-day selection`.

    startdate = `2018-07-09T00:00:00`.
    t_appointments = VALUE #(
      ( title = `Discussion of the plan`                            text = ``               type = `Type01` icon = ``                        startdate = `2018-07-09T00:00:00` enddate = `2018-07-09T00:00:00` )
      ( title = `Meet John Miller`                                  text = ``               type = `Type05` icon = ``                        startdate = `2018-07-08T05:00:00` enddate = `2018-07-08T06:00:00` )
      ( title = `Lunch`                                             text = `canteen`        type = `Type05` icon = ``                        startdate = `2018-07-08T07:00:00` enddate = `2018-07-08T08:00:00` )
      ( title = `New Product`                                       text = `room 105`       type = `Type01` icon = `sap-icon://meeting-room` startdate = `2018-07-08T08:00:00` enddate = `2018-07-08T09:00:00` )
      ( title = `Discussion with clients for the new release dates` text = `Online meeting` type = `Type08` icon = ``                        startdate = `2018-07-09T09:00:00` enddate = `2018-07-09T10:00:00` )
      ( title = `Meeting with the manager`                          text = ``               type = `Type03` icon = ``                        startdate = `2018-07-06T09:00:00` enddate = `2018-07-06T10:00:00` )
      ( title = `Daily standup meeting`                             text = ``               type = `Type01` icon = ``                        startdate = `2018-07-07T10:00:00` enddate = `2018-07-07T10:30:00` )
      ( title = `Private meeting`                                   text = ``               type = `Type03` icon = ``                        startdate = `2018-07-06T11:30:00` enddate = `2018-07-06T12:00:00` )
      ( title = `Lunch`                                             text = ``               type = `Type05` icon = ``                        startdate = `2018-07-06T12:00:00` enddate = `2018-07-06T13:00:00` )
      ( title = `Discussion of the plan`                            text = ``               type = `Type01` icon = ``                        startdate = `2018-07-16T11:00:00` enddate = `2018-07-16T12:00:00` )
      ( title = `Lunch`                                             text = `canteen`        type = `Type05` icon = ``                        startdate = `2018-07-16T12:00:00` enddate = `2018-07-16T13:00:00` ) ).

  ENDMETHOD.

ENDCLASS.