GridSwiper - globules-io/OGX.JS GitHub Wiki

GridSwiper is a dual axis infinite (or limited) swiper. This component re-uses its 5 grid cells rendered in a cross pattern and moves them around depending on the direction of the swipe for performance rendering in mind, just like a tile engine.

GridSwiper renders content in its virtual grid based on a map (and limited to that map's depth/size), or based on content added every time a swipe has completed.

Stack

Extends

 Uxi, Touch

OML

 {"selector:GridSwiper":{CONFIG}}

Create

 let config = {
      el:_SELECTOR_, //Required, the HTML element  
      core:_OGX_APP_, //OGX.App instance, required if used within app with views
      map:_ARRAY_, //Optional, data map         
      start:_POINT_, //Optional, a 2D point coordinates of the grid cell to show at start, defaults to center of map
      loop:_BOOL_, //Optional, infinite loop
      axis:_OBJECT_, //Optional, object to enable/disable axis, defaults to {x:true, y:true}
      chromeos:_BOOL_ //Optional, set to true if used on ChromeOS app
 };

As an OML node

 let swiper = OGX.OM.render(this, OML);

At runtime, from an object extending UXi

 let swiper = this.create('GridSwiper', config);

Independently

 let swiper = OGX.Object.create('GridSwiper', config);

Map

In order to tell the component what to render at start and then what to render as the end user swipes the grid, you need to work with a 2D map. If you simply want to horizontally swipe between 2 panels or views, check out Carousel

As a 2D array is passed then the depth restriction is based on the length of the series around its starting point. In other words, the component will display content BB upon start, a swipe down would display content BA and disable further swiping down. Then swiping right would display content AA and disable further swiping right.

 let config = {..., map:[
      ['content AA', 'content BA', 'content CA'],
      ['content AB', 'content BB', 'content CB'],
      ['content AC', 'content BC', 'content CC'],
 ]};

Disabled cell

If you wish to prevent the end user to land on a particular cell, set the map data to false for that cell. For instance, if we change the map to the following, the end user will be restricted from reaching the top left cell

 let config = {..., map:[
      [false, 'content BA', 'content CA'],
      ['content AB', 'content BB', 'content CB'],
      ['content AC', 'content BC', 'content CC'],
 ]};

Content as OML

You can of course use OML instead of html content. In this case, the views will receive a call to their focus/blur method depending on their individual position. If a view, due to the result of a swipe, is not displayed anymore, it will receive a call to its blur method. If the view if outside of the surroundings of the displayed cell, that view will get destroyed.

If you are using OML, your map would look like this

 {..., map:[[
      {"default:Views.MyView":{template:'MyTemplate', data:SomeDataA}}, 
      {"default:Views.OtherView":{template:'MyTemplate', data:SomeDataB}}, 
      {"default:Views.ThirdView":{template:'MyTemplate', data:SomeDataC}}, 
 ]]};

You can also pass dynamic data based on the cell position by using a custom function. Consider this simple function that will return a different object based on the value of the passed relative cell point

 function customData(relative_point, absolute_point, offset_point){
       if(relative_point.x > 5){
             return {whatever:'whatever'};
       }
       return {whatever:'breh'};
 }

Now you can use this function to generate custom data for a view by settings it in the map, such as

  {..., map:[[
      {"default:Views.MyView":{template:'MyTemplate', data:customData}}, 
      {"default:Views.OtherView":{template:'MyTemplate', data:customData}}, 
      {"default:Views.ThirdView":{template:'MyTemplate', data:customData}}, 
 ]]};

In this case, the OML nodes will be passed the result of customData as it is instanced in a cell of the grid

Content as custom function

You can also render cells content using custom functions. Custom functions receive the relative and absolute grid point coordinates and the HTML element representing the content of the cell. Consider the following custom function

 function renderStuff(relative_point, absolute_point, offset_point, element){
      var html = 'My custom content for grid cell at '+relative_point.x+','+relative_point.y;
      return html;
 }

This is a very simple function that will return a html string with the passed coordinates. Up to you to make your own calculations based on the given points, to return a html string that would vary on the coordinates of the points. Then to use it, simply add it to your map

 let config = {..., map:[
      [renderStuff, 'content BA', 'content CA'],
      ['content AB', 'content BB', 'content CB'],
      ['content AC', 'content BC', renderStuff],
 ]};

In this configuration, the custom function will be called when displaying the top left of the map or the bottom right.

Difference between points

relative_point is the point relative to your map. Loops are ignored here and the point will always be relative to your map length. This point will never pass the width/height of the map.

absolute_point is the overall offset based on the grid system. Depending on your map, that point might be the same as the relative_point at start, but in a loop situation, the absolute_point will keep increasing/decreasing passed the width/height of the map.

offset_point is used to indicate the position of a cell around the centered cell, such as {x:-1, y:0} to indicate that the cell is left of the centered cell, {x:0, y:1} would indicate the cell is below the centered cell.

Content on-the-fly

If you want to work in a true unlimited grid without passing all of the content in a map, you can add content on the fly. In this case, you would set your map to display the initial cells (start cell and the cells around it in a cross pattern), and then update the map after every swipe.

 let config = {..., map:[
      ['', 'content BA', ''],
      ['content AB', 'content BB', 'content CB'],
      ['', 'content BC', ''],
 ]};

Then you would listen to a swipe event and then from that event, update the map content, such as

 swiper.el.on(OGX.GridSwiper.SWIPE_RIGHT, function(__e, __data){
       //__data.cell = the relative map cell
       //__data.point = the absolute map point

       //set some content on the fly for the cell on the left
       swiper.setLeftCell('some html content');    

 });

The data object that is passed upon a swipe contains the relative and absolute coordinates. The cell property holds the relative map cell, meaning that no matter how many times you have looped, you will get coordinates within the map range.

The point property holds the absolute point, which can be negative. For instance, if the end user swipes right enough time to loop, the x coordinate might be negative.

Now, if you use this method, understand that you must be passing content that is not yet visible, around the visible cell, based on the direction of a swipe.

Loop

If you wish to have a the grid infinitely displaying your content in a loop, set the loop flag to true in the config. If set to false, the component will prevent from swiping in a direction outside of the map bounds.

Start

If a start point is not passed, the component will calculate and use the center of the map as the starting point. If you wish to start to a specific location, pass a start point corresponding to the coordinates of the cell to display, such as

 let config = {..., start:{x:0, y:0}, ...}

Axis

You can also force an axis to be disabled by using the axis flag. The following disables swipping vertically, no matter if the map has multiple rows. Note that using a map with only 1 row automatically disables the vertical axis

 let config = {..., axis:{x:true, y:false}, ...}

Methods

 swiper.swipeRight();
 swiper.swipeLeft();
 swiper.swipeUp();
 swiper.swipeDown();
 swiper.setMap(__map, __start_pt); //Set map (2DArray) and optional start point
 swiper.setMapCell(__point, __data); //Set or update data in map
 swiper.getCells(); //Return rendered cells as {abs:point, abs:point, offset:abs:point, element:HTMelement}
 swiper.setTopCell(_content_); //Set the content for cell top to current cell
 swiper.setBottomCell(_content_); //Set the content for cell bottom to current cell
 swiper.setLeftCell(_content_); //Set the content for the cell left to current cell
 swiper.setRightCell(_content_); //Set the content for the cell right to current cell

Events

 OGX.GridSwiper.SWIPE_RIGHT
 OGX.GridSwiper.SWIPE_LEFT
 OGX.GridSwiper.SWIPE_UP
 OGX.GridSwiper.SWIPE_DOWN
 OGX.GridSwiper.SWIPE_START
 OGX.GridSwiper.SWIPE_END

Enable/Disable

 swiper.enable();
 swiper.disable();

Destroy

 swiper.destroy();