# About g1 `g1` is library of interaction patterns in [Gramex](https://learn.gramener.com/guide/). Install using: yarn install g1 # ... OR ... npm install --save g1 To use all features, add this to your HTML: Or import one of the individual libraries below. [g1.min.js](dist/g1.min.js) has all of these functions. [g.js](dist/g.js) is an un-minified version for debugging. Interactions: - [urlfilter.min.js](dist/urlfilter.min.js): URL filtering library - [$.urlfilter](#urlfilter) changes URL query parameters when clicked. Used to filter data. - [g1.url.parse](#urlparse) parses a URL into a structured object - [g1.url.join](#urljoin) joins two URLs - [g1.url.update](#urlupdate) updates a URL's query parameters - [highlight.min.js](dist/highlight.min.js): highlighting library - [$.highlight](#highlight) toggles classes on elements when clicked or hover - [search.min.js](dist/search.min.js): search-as-you-type library - [$.search](#search) - [datafilter.min.js](dist/datafilter.min.js): filtering data library - [$.datafilter](#datafilter) filters the data based on the options Data components: - [formhandler.min.js](dist/formhandler.min.js): Table renderer using [FormHandler](https://learn.gramener.com/guide/formhandler/) - [$.formhandler](#formhandler) renders a HTML table from a [FormHandler URL](https://learn.gramener.com/guide/formhandler/) - [leaflet.min.js](dist/leaflet.min.js): Leaflet utilities - [L.TopoJSON](#ltopojson) loads TopoJSON files just like GeoJSON. Requires [topojson](https://github.com/topojson/topojson) Utilities: - [template.min.js](dist/template.min.js): template library - [$.template](#template) renders lodash templates. Requires [lodash](https://lodash.com/) - [event.min.js](dist/event.min.js): event library - [$.dispatch](#dispatch) is like [trigger](https://api.jquery.com/trigger/) but sends a native event (triggers non-jQuery events too) - [types.min.js](dist/types.min.js): type detection library - [g1.types](#types) returns the data types of columns in a DataFrames ## $.urlfilter Example: ```html Link ``` Let's say the page is `?city=NY`. Clicking on any `.urlfilter` (trigger) in `body` (container) opens `?city=NY&name=John`. The `href=` in the `.urlfilter` link *updates* the current page URL instead of replacing it. `data-mode` controls the way the URL is updated by the `href`: | URL | href | default | `data-mode="add"` | `data-mode="toggle"` | `data-mode="del"` | |--------|-----------|------------|-------------------|----------------------|-------------------| | `?` | `?x=1` | `?x=1` | `?x=1` | `?x=1` | `?` | | `?x=1` | `?x=1` | `?x=1` | `?x=1&x=1` | `?` | `?` | | `?x=1` | `?y=1` | `?x=1&y=1` | `?x=1&y=1` | `?x=1&y=1` | `?x=1` | | `?x=1` | `?x=2` | `?x=2` | `?x=1&x=2` | `?x=1&x=2` | `?x=1` | For example: ```html Change ?city= to NY Add ?city= to NY Remove NY from ?city= Toggle NY in ?city= Change ?city= to NY using pushState Change location.hash, i.e. #?city= to NY Change iframe URL ?city= NY Use AJAX to load ?city=NY into .block
``` ### $.urlfilter attributes URLFilter triggers use these attributes: - `class="urlfilter"` indicates that this is a trigger - `href=` updates the page URL with this link - `data-target` defines the target where the URL is updated: - default: updates `window.location` - `pushState`: updates the current page using pushState - `#`: updates the `window.location.hash` - `.class`: loads URL into `.class` by updating its `src=` attribute as the base URL - `data-mode` can be - empty - updates existing query key with value - `add` - adds a new query key and value - `del` - deletes query key. If value exists, only deletes the (key, value) combination - `toggle` - toggles the query key and value combination - `data-remove`: removes any URL query parameters without values. e.g. `?x&y=1` becomes `?` - `data-src` changes which attribute holds the current URL when `data-target=` is a selector. Default: `src` URLFilter containers uses these attributes: - `data-selector` changes which nodes urlfilter applies to. Default: `.urlfilter` - `data-attr` changes which attribute updates the URL. Default: `href` - You can also specify `data-mode`, `data-remove` and `data-src`. This is the same as specifying on every `.urlfilter` trigger. ### $.urlfilter events - `urlfilter` is fired on the trigger when the URL is changed. Attributes: - `url`: the new URL - `load` is fired on the target when the URL is loaded -- only if the `data-target=` is a selector. Attributes: - `url`: the new URL ## $.highlight Highlight elements when hovering on or clicking another element. Example: ```html
Link
Link ``` ### $.highlight attributes Highlight triggers use these attributes: - `data-toggle="highlight"` indicates that the element acts as a highlighter - `data-target=` selectors to highlight (required) - `data-mode="click"` highlights on click. Use `data-mode="hover"` to higlight on hover (default) - `data-classes=` space-separated class names to toggle on target elements Highlight containers use these attributes: - `data-selector=` changes which nodes highlight applies to. Default: `[data-toggle="highlight"]` - `data-mode` is the same as specifying data-mode on every highlighter. Default: `hover` - `data-attr` is the attribute that defines classes to toggle. Default: `data-classes` - `data-classes=` is the same as specifiying the data-classes on every highlighter. Default: `active` ### $.highlight events - `highlight` is fired on the trigger when activated. Attributes: - `target`: elements that match the `data-target=` selector ## $.search Highlight elements by searching as you type. Example: ### $.search attributes `.search` triggers use these attributes: - `data-toggle="search"` ## datafilter `g1.datafiilter(data, filters)` returns the filtered data based on the filters. For example: ```js var data = [ {"ID": "1", "product": "Fan", "sales": "100", "city": "NY"}, {"ID": "2", "product": "Fan", "sales": "80", "city": "London"}, {"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"}, {"ID": "4", "product": "Fan", "sales": "130", "city": "London"}, {"ID": "5", "product": "Light", "sales": "500", "city": "NY"}, {"ID": "5", "product": "Light", "sales": "100", "city": "London"} ] g1.datafilter(data, [{col: 'sales', op: '>', val: 100}, {col: 'city', op: 'in', val: ['London', 'NY']}, {col: 'product', val: 'Fan'}]) // Returns [{"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"}, {"ID": "4", "product": "Fan", "sales": "130", "city": "London"}] ``` ## datafilter options datafilter() contains three parameters: - data: a list of objects - filters: a list of objects, that will contains the below keys: - col: column to be filtered. - op: operator to be applied for filteration. default: `=` - val: value of the selected column - options: a dictionary that contains the below keys: - limit: result is limited to. default: `1000` - offset: filtering data should start from. default: `0` - sort: a list of objects, that will contains the below keys: - column: column to be sorted - order: asc or desc. default: `asc` - columns: a list of objects, that will contains the below keys: - allow: a list of column names to be returned in the filtered data - not: a list of column names to be skiped in the filtered data Rules: - the key `op` may contains any one of the below values: - `=` - `!=` - `>` - `<` - `>=` - `<=` - `~` - `!~` - `in` ## $.formhandler An interactive table component for [FormHandler][formhandler] data. ```html
``` Options can passed via an options dict, and over-ridden using `data-` attributes. In the above example, `data-page-size="10"` over-rides `pageSize: 20`. [formhandler]: https://learn.gramener.com/guide/formhandler/ ### $.formhandler options The full list of options is below. Simple options can be specified as `data-` attributes as well. - `src`: [FormHandler][formhandler] URL endpoint - `columns`: comma-separated column names to display, or a list of objects with these keys: - `name`: column name. `"*"` is a special column placeholder for "all columns" - `title`: for header display. Defaults to the same value as `name` - `type`: `text` (default) / `number` / `date`. Data type. Determines filters to be used - `format`: string / function that renders the cell contents. - functions are applied to the value and the return value is used - strings specify a numeral.js format if the value is a number (you must include numeral.js) - strings specify a moment.js format if the value is a date (you must include moment.js) - `sort`: `true` / `false` / operators dict with: - `{'': 'Sort ascending', '-': 'Sort descending'}` (default) - `filters`: `true` (default) / `false` / operators dict with: - `{'', 'Equals...', '!', 'Does not equal...', ...}`. The default list of operators is based on the auto-detected type of the column. - `link`: string / function that generates a link for this each cell. - If no `link:` is specified, clicking on the cell filters by that cell. - If `link:` is a string, opens a new window with the string URL interpolated as a lodash template with `row` as data. Example: `"https://example.org/city/<%- city >"` - If `link:` is a function, opens a new window with the URL as `fn(row)`. Example: `function(row) { return 'https://example.org/city/' + row.city }` - `hideable`: `true` (default) / `false`. Hides the column - `unique`: TODO: {dict of query parameter and display value} or [list of values] or function? - `table`: Shows the table control. Can be `true` (default) / `false` - `count`: Shows the number of rows. Can be `true` (default) / `false` - `page`: Shows the page control. Can be `true` (default) / `false`. - `pageSize`: page size. Defaults to 100 - `size`: Shows the page size control. Can be `true` (default) / `false` - `sizeValues`: Allowed page size values. Defaults to `[10, 20, 50, 100, 500, 1000]` - `export`: Shows the export control. Can be `true` (default) / `false` - `exportFormats`: {xlsx: 'Excel'} - `filters`: Shows the applied filters control. Can be `true` (default) / `false` - `transform`: an optional function() that modifies data. It accepts a dict that has keys: - `data`: the FormHandler data - `meta`: the FormHandler metadata from the `FH-*` HTTP headers - `args`: the URL query parameters passed to the FormHandler - `options`: the options applicable to the FormHandler - returns a dict with modified values of `data` and `meta` **Advanced**. Each component can have a target which specifies a selector. For e.g., to render the export button somewhere else, use `data-export-target=".navbar-export"`. This replaces the `.navbar-export` contents with the export button. (It searches within the table container for `.navbar-export` first, and if not found, searches everywhere.) Available targets are: - `tableTarget` - `countTarget` - `pageTarget` - `sizeTarget` - `exportTarget` - `filtersTarget` - `searchTarget` **Advanced**: Each component's template string can be over-ridden. For example, `data-search-template=""` will replace the search template with a simple input. Available template strings are: - `tableTemplate` - `countTemplate` - `pageTemplate` - `sizeTemplate` - `exportTemplate` - `filtersTemplate` - `searchTemplate` Features to be implemented: - Loading indicator - Full text search - URL prefix / namespace, if there are multiple tables on the same page - URL targets other than '#', e.g. pushState ### $.formhandler events - `load` is fired on the source when the template is rendered. Attributes: - `formdata`: the FormHandler data - `meta`: the FormHandler metadata - `args`: the URL query parameters passed to the request - `options`: applied options to the FormHandler ### $.formhandler examples Add a simple table using the FormHandler at `./data` that shows specific columns with a page size of 10 rows, and does not show the export filter. ```html
``` ## $.template ```html ``` renders all `script[type="text/html"]` as [lodash templates](https://lodash.com/docs/#template). This displays `Your platform is ...` and shows the userAgent just below the script tag. - Anything inside `<% ... %>` is executed as Javascript. - Anything inside `<%= ... %>` is evaluated in-place. The template can use all global variables. You can pass additional variables using as `.template({var1: value, var2: value, ...})`. For example: ```html ``` To re-render the template, run `.template(data)` again with different data. ## $.template options To re-use the template, i.e. render the same template on a different DOM node, run `.template(data, {target: selector})`. This allows you to declare templates once and apply them across the body. For example: ```js $('script.chart') .template({heading: 'Dashboard 1'}, {target: '.dashboard1'}) .template({heading: 'Dashboard 2'}, {target: '.dashboard2'}) .template({}, {target: '.no-heading'}) ``` ## $.template attributes Template containers can have an `src=` attribute that loads the template from a file: ```html ``` If the `src=` URL returns a HTTP error, the HTML *inside* the script is rendered as a template. The template can use: - all data passed by the `$().template()` function, and - an [xhr](http://api.jquery.com/Types/#jqXHR) object - which has error details For example: ```html ``` `$().template()` renders all `script[type="text/html"]` nodes. Use `data-selector=` attribute to change the selector. For example: ```html
``` Or you can directly render templates using ```html ``` ### $.template events - `template` is fired on the source when the template is rendered. Attributes: - `templatedata`: the passed data - `target`: the target nodes rendered, as a jQuery object For example: ```js $('script[type="text/html"]') .on('template', function(e) { // Returns nodes rendered by the template e.target // Get the target nodes .filter('div') // Filter all
elements inside .attr('class', 'item') // Change their class }) .template() // Trigger the template AFTER binding the event handler ``` ## $.dispatch Triggers a native JavaScript event. For example: ```js $('a.action').dispatch('click') ``` sends a click to `a.action`. Like [$.trigger](https://api.jquery.com/trigger/), but this will fire non-jQuery event handlers as well. ### $.dispatch options You can add an optional dict as the second parameter. It can have any [event properties](https://developer.mozilla.org/en-US/docs/Web/API/Event#Properties) as attributes. For example: ```js $('a.action').dispatch('click', {bubbles: true, cancelable: false}) ``` - bubbles: whether the event bubbles or not. default: true - cancelable: whether the event is cancelable or not. default: true - All other `new Event()` options will also work https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events ## L.TopoJSON ```js var layer = new L.TopoJSON(topojson_data).addTo(map) ``` adds a TopoJSON layer to a leaflet map. The usage is identical to [L.GeoJSON()](http://leafletjs.com/reference-1.2.0.html#geojson). Typical usage is below: ```js $.get('topojson-file.json') .done(function(topojson_data) { var map = L.map('map-id') var layer = new L.TopoJSON(topojson_data).addTo(map) map.fitBounds(layer.getBounds()) }) ``` ## url.parse `g1.url` provides URL manipulation utilities. ```js var url = g1.url.parse("https://username:password@example.com:80/~folder/subfolder/filename.html?a=1&a=2&b=3%2E&d#hash") ``` ### url object attributes This parses the URL and returns an object with the following attributes matching `window.location`: | Attribute | Value | |------------|------------------------------------| | `href` | the original URL | | `protocol` | `https` | | `origin` | `username:password@example.com:80` | | `username` | `username` | | `password` | `password` | | `hostname` | `example.com` | | `port` | `80` | | `pathname` | `folder/subfolder/filename.html` | | `search` | `a=1&a=2&b=3%2E&d` | | `hash` | `hash` | ... and additional attributes: | Attribute | Value | |--------------|--------------------------------------------------------| | `userinfo` | `username:password` | | `relative` | `folder/subfolder/filename.html?a=1&a=2&b=3%2E&d#hash` | | `directory` | `folder/subfolder/` | | `file` | `filename.html` | | `searchKey` | `{'a:'2', b:'3.', d:''}` | | `searchList` | `{'a:['1', '2'], b:['3.'], d:['']}` | It can also parse URL query strings. ```js var url = g1.url.parse('?a=1&a=2&b=3%2E&d#hash') ``` | Attribute | Value | |--------------|----------------------------------| | `search` | `a=1&a=2&b=3%2E&d` | | `hash` | `hash` | | `searchKey` | `{a:'2', b:'3.', d:''}` | | `searchList` | `a:['1', '2'], b:['3.'], d:['']` | These attributes are **not mutable**. To change the URL, use [url.join](#urljoin) or [url.update](#urlupdate). ### url object methods The url object has a `.toString()` method that converts the object back into a string. ## url.join ```js var url = url.join(another_url) ``` updates the `url` with the attributes from `another_url`. For example: | url | joined with | gives | |------------------------|----------------------|----------------------------| | `/path/p` | `a/b/c` | `/path/a/b/c` | | `/path/p/q/` | `../a/..` | `/path/p/` | | `http://host1/p` | `http://host2/q` | `http://host2/q` | | `https://a:b@host1/p` | `//c:d@host2/q?x=1` | `https://c:d@host2/q?x=1` | | `/path/p?b=1` | `./?a=1#top` | `/path/?a=1#top` | `.join()` updates the query parameters and hash fragment as well. To prevent this, use: ```js url.join(another_url, {query: false, hash: false}) ``` For example: ```js g1.url.parse('/').join('/?x=1#y=1', {hash: false}).toString() == '/?x=1'; g1.url.parse('/').join('/?x=1#y=1', {query: false}).toString() == '/#y=1'; ``` ## url.update ```js var url = url.update(object) ``` updates the `url` query parameters with the attributes from `object`. For example: | url | updated with | gives | |--------------|----------------------|-----------------------| | `/` | `{a:1}` | `/?a=1` | | `/?a=1&b=2` | `{b:3, a:4, c:''}` | `/?a=4&b=3&c=` | | `/?a=1&b=2` | `{a:null}` | `/?b=2` | | `/?a=1&b=2` | `{a:[3,4], b:[4,5]}` | `/?a=3&a=4&b=4&b=5` | By default, it *updates* the query parameters. But: - `url.update(object, 'add')` *adds* the query parameters instead of updating - `url.update(object, 'del')` *deletes* the query parameters instead of updating - `url.update(object, 'toggle')` *toggles* the query parameters (i.e. adds if missing, deletes if present) For example: | url | updated with | in mode | gives | |---------------------|----------------------|---------------------|-----------------------| | `/?a=1&a=2` | `{a:3, b:1}` | `add` | `/?a=1&a=2&a=3&b=1` | | `/?a=1&a=2'` | `{a:[3,4]}` | `add` | `/?a=1&a=2&a=3&a=4` | | `/?a=1&a=2&b=1` | `{a:2, b:2}` | `del` | `/?a=1&b=1` | | `/?a=1&a=2&b=1` | `{a:[1,4]}` | `del` | `/?a=2&b=1` | | `/?a=1&a=2` | `{a:1, b:1}` | `toggle` | `/?a=2&b=1` | | `/?a=1&a=2&b=1&b=2` | `{a:[2,3], b:[1,3]}` | `toggle` | `/?a=1&a=3&b=2&b=3` | You can specify different modes for different query parameters. ```js g1.url.parse('/?a=1&b=2&c=3&d=4') // Update this URL .update({a:1, b:[2,3], c:6, d:7}, // With this object 'a=del&b=toggle&c=add') // Delete ?a, Toggle ?b, add ?c, update ?d (default) // Returns /?b=3&c=3&c=6&d=7 ``` ## types `g1.types(data)` returns the column data types. For example: ```js var data = [ {a: 1, b: 1.1, c: 'c', d: '2014-04-04', e: true}, {a: 2, b: 2}, ] g1.types(data) // Returns {"a": "number", "b": "number", "c": "string", "d": "date", "e": "boolean"} ``` ### types options `types()` accepts 2 parameters: - `data`: a list of objects - `options`: a dictionary that may contain these keys: - `convert`: converts values to the right type. For example, "1" is converted to 1. default: `false` - `limit`: number of rows to evaluate. default: 1000 - `ignore`: list of values that should be ignored. default: `[null, undefined]` Rules: - Evaluate up to `limit` rows - Ignore values that are keys in the `ignore` option. Only consider the rest - If `convert` is `false`, then for each column: - If all values are Date objects -> `date` - Else if all values are numbers -> `number` - Else if all values are strings -> `string` - Else if all values are bools -> `boolean` - Else if there are no values or is undefined or null -> `null` - Else -> `mixed` - Else if `convert` is `true`, then for each column: - If all values can be converted to Date -> `date` - Else if all values can be converted to numbers -> `number` - Else if all values are bools -> `boolean` - Else if there are no values or is undefined or null -> `null` - Else -> `string` ## Interaction conventions All interaction components use this naming convention: - Interactions are enabled on a *container*, typically `body`. For example, `$('body').urlfilter()`. Containers have these common attributes: - `data-selector`: selector to identify triggers. e.g. `.urlfilter`, `.highlight` - `data-target`: selector that all triggers act on by default - `data-mode`: mode of interaction for all triggers - `data-attr`: attribute that contains the interaction data, e.g. `href` for `.urlfilter` - Interactions are triggered on a *trigger*. For example, `.urlfilter` for `$().urlfilter()`. Clicking / hovering on / typing in a trigger triggers the interaction. - `data-target`: selector that this trigger acts on - `data-mode`: mode of interaction for this trigger - Interactions change a *target*. For example, `urlfilter` changes `location.href` by default. The `data-target` on containers and triggers define this. - Interactions data is contained in an attribute. This is applied to the target. For example, `.urlfilter` applied `href` to the target. The attribute name is stored in `data-attr`. - Interactions have *modes*. This can be controlled using `data-mode=`. All container `data-` attributes can also be passed as an option to the function. For example, `` is the same as `$('body').urlfilter({selector: '.link'})`. ## Contributing Contributions are welcome. - Please report bug fixes on the [issues page](https://code.gramener.com/s.anand/g1/issues) - Developers may read [CONTRIBUTING.md](CONTRIBUTING.md) to understand the file structure and how to set this repository up