Samples / Controls / sap.ui.unified

Multiple Day Selection

Calendar where the user can select multiple days, entire weeks (either by selecting its week number or by using SHIFT + Space) and ranges (using SHIFT + ENTER/Left mouse click).

Class
z2ui5_cl_smpc_app_307
Repository
abap2UI5/samples-controls
Ports
sap.ui.unified.sample.CalendarMultipleDaySelection
UI5 libraries
sap.m, sap.ui.core, sap.ui.layout, sap.ui.unified
Lowest UI5 release
1.71
In the browser
Runs in the playground
Controls it builds
sap.m.Button, sap.m.List, sap.m.StandardListItem, sap.ui.core.HTML, sap.ui.core.mvc.View, sap.ui.layout.VerticalLayout, sap.ui.unified.Calendar

The class

" @keywords calendar sap.ui.unified calendarmultipledayselection html verticallayout button list standardlistitem
" @summary Calendar where the user can select multiple days, entire weeks (either by selecting its week number or by using SHIFT + Space) and ranges (using SHIFT + ENTER/Left mouse click).
" @origin sap.ui.unified.sample.CalendarMultipleDaySelection - https://sdk.openui5.org/entity/sap.ui.unified.Calendar/sample/sap.ui.unified.sample.CalendarMultipleDaySelection (status: reviewed - read against the original, not run)
CLASS z2ui5_cl_smpc_app_307 DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES z2ui5_if_app.

    TYPES:
      BEGIN OF ty_s_date,
        date TYPE string,
      END OF ty_s_date.
    DATA selecteddates TYPE STANDARD TABLE OF ty_s_date WITH EMPTY KEY.

  PROTECTED SECTION.
    " one entry per DateRange the frontend marshalled out of the LIVE
    " selectedDates aggregation - 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.

  PRIVATE SECTION.
ENDCLASS.


CLASS z2ui5_cl_smpc_app_307 IMPLEMENTATION.

  METHOD z2ui5_if_app~main.

    me->client = client.
    IF 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( ).

    view->ele( n = `View` ns = `mvc`
        )->a( n = `xmlns:l`    v = `sap.ui.layout`
        )->a( n = `xmlns:u`    v = `sap.ui.unified`
        )->a( n = `xmlns:mvc`  v = `sap.ui.core.mvc`
        )->a( n = `xmlns`      v = `sap.m`
        )->a( n = `class`      v = `viewPadding`

        )->a( n = `xmlns:core` v = `sap.ui.core`

        " the sample's own ../style.css (shared by the sap.ui.unified samples and
        " listed in this sample's manifest) - the view carries the class and the
        " rule behind it has to come with it. \{ \} escaped: the XMLView parser
        " reads an unescaped brace as a binding
        )->tag( n = `HTML` ns = `core`
            )->a( n = `content` v = `<style>.viewPadding\{padding:1rem\}` &&
                                    `.sap-phone .viewPadding\{padding:0rem\}` &&
                                    `.sap-phone .sapUiCal\{position:relative\}` &&
                                    `.labelMarginLeft\{margin:1rem\}</style>`
        )->ele( n = `VerticalLayout` ns = `l`

            )->tag( n = `Calendar` ns = `u`
                )->a( n = `id`                v = `calendar`
                " the WHOLE selectedDates aggregation 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 = `select`            v = client->_event( val = `CAL_SELECT` arg = `$event.oSource.getSelectedDates()` )
                )->a( n = `intervalSelection` v = `false`
                )->a( n = `singleSelection`   v = `false`
            )->tag( `Button`
                )->a( n = `press` v = client->_event( `REMOVE_SELECTION` )
                )->a( n = `text`  v = `Remove All Selected Dates`

            )->ele( `List`
                )->a( n = `id`         v = `selectedDatesList`
                )->a( n = `class`      v = `labelMarginLeft`
                )->a( n = `noDataText` v = `No Dates Selected`
                )->a( n = `headerText` v = `Selected Dates (yyyy-mm-dd)`
                )->a( n = `items`      v = |\{path: '{ client->_bind_path( selecteddates ) }'\}|

                )->tag( `StandardListItem`
                    )->a( n = `title` v = `{DATE}` ).

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

  ENDMETHOD.


  METHOD on_event.

    CASE client->get_event( ).

      WHEN `CAL_SELECT`.
        " handleCalendarSelect: rebuild the model from EVERY selected date,
        " each formatted yyyy-MM-dd - the day is the first ten characters of
        " the ISO local timestamp the marshalled DateRange carries
        selecteddates = VALUE #( ).
        DATA(ranges) = event_ranges( client->get_event_arg( ) ).
        LOOP AT ranges REFERENCE INTO DATA(range).
          IF strlen( range->startdate ) < 10.
            CONTINUE.
          ENDIF.
          INSERT VALUE #( date = range->startdate(10) ) INTO TABLE selecteddates.
        ENDLOOP.

      WHEN `REMOVE_SELECTION`.
        " handleRemoveSelection: removeAllSelectedDates( ) + clear the model.
        " selectedDates is written by the control itself, so the aggregation
        " has to be emptied on the control - the model half alone would leave
        " the days highlighted
        selecteddates = VALUE #( ).
        client->follow_up_action( val   = client->cs_event-control_by_id
                                  t_arg = VALUE #( ( `calendar` ) ( `removeAllSelectedDates` ) ) ).

    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.

ENDCLASS.