Commit c66db787 authored by S Anand's avatar S Anand
Browse files

ENH: .highlight() highlights elements. Fixes #8

parent a6cfaa6a
Pipeline #41338 passed with stage
in 1 minute and 35 seconds
......@@ -15,19 +15,31 @@ 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)
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)
- [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)
## $.urlfilter
......@@ -40,9 +52,9 @@ Example:
</script>
```
Let's say the page is `?city=NY`. Clicking on any `.urlfilter` in `body` opens
`?city=NY&name=John`. The `href=` in the `.urlfilter` link *updates* the current
page URL instead of replacing it.
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`:
......@@ -53,56 +65,108 @@ page URL instead of replacing it.
| `?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
<a class="urlfilter" href="city=NY"> Change ?city= to NY</a>
<a class="urlfilter" href="city=NY" data-mode="add"> Add ?city= to NY</a>
<a class="urlfilter" href="city=NY" data-mode="del"> Remove NY from ?city=</a>
<a class="urlfilter" href="city=NY" data-mode="toggle"> Toggle NY in ?city=</a>
<a class="urlfilter" href="city=NY" data-target="pushState">Change ?city= to NY using pushState</a>
<a class="urlfilter" href="city=NY" data-target="#"> Change location.hash, i.e. #?city= to NY</a>
<a class="urlfilter" href="city=NY" data-target="iframe"> Change iframe URL ?city= NY</a>
<iframe src="?country=US"></iframe>
<a class="urlfilter" href="city=NY" data-target=".block"> Use AJAX to load ?city=NY into .block</a>
<div class="block" src="?country=US"></div>
<script>
$('body').urlfilter() // Activate all the .urlfilter elements above
</script>
```
### $.urlfilter attributes
A `.urlfilter` class can use these attributes:
URLFilter triggers use these attributes:
- `href=` updates the URL
- `data-target=` defines the target where the URL is updated:
- `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`
The element on which `.urlfilter()` is called can have these attributes:
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`
- `data-src=` changes which attribute holds the current URL when `data-target=` is a selector. Default: `src`
- `data-remove` is the same as specifying data-remove on every `.urlfilter`. Default: none
- `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 source when the URL is changed. Attributes:
- `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
### $.urlfilter examples
Highlight elements when hovering on or clicking another element.
Add this line to the page:
Example:
```js
$('body').urlfilter()
```html
<div data-toggle="highlight" data-target="a.red" data-classes="active">Link</a>
<div data-toggle="highlight" data-target="a.red" data-classes="active" data-mode="click">Link</a>
<script>
$('body').highlight({
classes: 'shadow',
mode: 'click'
})
</script>
```
This activates all `.urlfilter` classes as below:
### $.highlight attributes
```html
<a class="urlfilter" href="city=NY"> Change ?city= to NY</a>
<a class="urlfilter" href="city=NY" data-mode="add"> Add ?city= to NY</a>
<a class="urlfilter" href="city=NY" data-mode="del"> Remove NY from ?city=</a>
<a class="urlfilter" href="city=NY" data-mode="toggle"> Toggle NY in ?city=</a>
Highlight triggers use these attributes:
<a class="urlfilter" href="city=NY" data-target="pushState">Change ?city= to NY using pushState</a>
<a class="urlfilter" href="city=NY" data-target="#"> Change location.hash, i.e. #?city= to NY</a>
<a class="urlfilter" href="city=NY" data-target="iframe"> Change iframe URL ?city= NY</a>
<iframe src="?country=US"></iframe>
<a class="urlfilter" href="city=NY" data-target=".block"> Use AJAX to load ?city=NY into .block</a>
<div class="block" src="?country=US"></div>
```
- `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"`
## $.formhandler
......@@ -506,6 +570,30 @@ g1.url.parse('/?a=1&b=2&c=3&d=4') // Update this URL
// Returns /?b=3&c=3&c=6&d=7
```
## 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.
Please register or sign in to reply
- `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, `<body data-selector=".link">` is the same as
`$('body').urlfilter({selector: '.link'})`.
## Contributing
Contributions are welcome.
......
export { version } from './src/package.js'
import { highlight } from './src/highlight.js'
if (typeof jQuery != 'undefined') {
jQuery.extend(jQuery.fn, {
highlight: highlight
})
}
export { version } from './src/package.js'
export { url } from './index-urlfilter.js'
import './index-highlight.js'
import './index-template.js'
import './index-formhandler.js'
import './index-event.js'
......
......@@ -26,6 +26,7 @@
"express": "4",
"faucet": "^0.0.1",
"font-awesome": "4",
"glob": "^7.1.2",
"html-minifier": "3",
"jquery": "3",
"json2module": "0.0",
......
......@@ -22,6 +22,11 @@ export default [
plugins: [htmlparts('src/formhandler.template.html'), uglify()],
output: { file: "dist/formhandler.min.js", format: "umd", name: "g1" }
},
{
input: "index-highlight",
plugins: [uglify()],
output: { file: "dist/highlight.min.js", format: "umd", name: "g1" }
},
{
input: "index-template",
plugins: [uglify()],
......
import { findall } from './_util.js'
var container_options = {
selector: '[data-toggle="highlight"]',
target: '.highlight-target',
mode: 'hover',
attr: '.data-classes',
classes: 'active'
}
export function highlight(options) {
this.each(function() {
var settings = $.extend({}, container_options, options, this.dataset)
// Loop through all triggers in the container. This may include the container itself
findall($(this), settings.selector).each(function () {
var opts = $.extend({}, settings, this.dataset)
var $this = $(this).off('.g1.highlight')
var event = opts.mode == 'click' ? 'click.g1.highlight' : 'mouseenter.g1.highlight mouseleave.g1.highlight'
$this.on(event, function () {
// When the trigger is triggered, toggle the target classes and fire a highlight event
var target = $(opts.target).toggleClass(opts.classes)
$this.trigger({ type: 'highlight', target: target })
})
})
})
return this
}
const path = require('path')
const express = require('express')
const paths = [
'test/test-urlfilter.html',
'test/test-formhandler.html',
'test/test-template.html',
'test/test-event.html',
'test/leaflet.topojson.html'
]
const glob = require('glob')
const port = process.argv.length <= 2 ? 1112 : 1111
const app = express()
......@@ -25,7 +19,7 @@ const server = app.listen(port, function () {
})
const page = await browser.newPage()
page.on('console', msg => console.log(msg.text))
const paths = glob.sync('test/test-*.html')
for (let i=0; i<paths.length; i++) {
let url = 'http://localhost:' + port + '/' + paths[i]
await page.goto(url)
......
......@@ -24,6 +24,7 @@
t.ok(typeof $.fn.urlfilter == 'function', '$.fn.urlfilter')
t.ok(typeof $.fn.formhandler == 'function', '$.fn.formhandler')
t.ok(typeof $.fn.highlight == 'function', '$.fn.highlight')
t.ok(typeof $.fn.template == 'function', '$.fn.template')
t.ok(typeof $.fn.dispatch == 'function', '$.fn.dispatch')
......
<!DOCTYPE html>
<html>
<head>
<title>highlight tests</title>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../dist/highlight.min.js"></script>
<script src="tape.js"></script>
<style>
.active { color: red; }
.blue { color: blue; }
</style>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="container1">
<div data-toggle="highlight" class="trigger1" data-target=".target1">trigger</div>
<em class="target1">target</em>
<div data-toggle="highlight" class="trigger2" data-target=".target2" data-mode="click">trigger</div>
<em class="target2">target</em>
<div data-toggle="highlight" class="trigger3" data-target=".target3" data-classes="blue">trigger</div>
<em class="target3">target</em>
</div>
<div class="container2" data-selector=".trigger4" data-mode="click" data-classes="blue" data-target=".target4">
<div class="trigger4">trigger</div>
<em class="target4">target</em>
</div>
<div class="container3">
<div class="trigger5">trigger</div>
<em class="target5">target</em>
</div>
<script>
function check(container, trigger, event, classes) {
return function(t) {
container.one('highlight', function(e) {
t.ok(e.target.is(classes))
t.end()
})
.find(trigger).trigger(event)
}
}
// Test target attributes
var $container1 = $('.container1').highlight()
tape('$().highlight() enter', check($container1, '.trigger1', 'mouseenter', '.target1.active'))
tape('$().highlight() leave', check($container1, '.trigger1', 'mouseleave', '.target1'))
tape('$().highlight() click', check($container1, '.trigger2', 'click', '.target2.active'))
tape('$().highlight() click', check($container1, '.trigger2', 'click', '.target2'))
tape('$().highlight() class enter', check($container1, '.trigger3', 'mouseenter', '.target3.blue'))
tape('$().highlight() class leave', check($container1, '.trigger3', 'mouseleave', '.target3'))
// Test container attributes
var $container2 = $('.container2').highlight()
tape('$().highlight() container click', check($container2, '.trigger4', 'click', '.target4.blue'))
tape('$().highlight() container click', check($container2, '.trigger4', 'click', '.target4'))
// Test highlight options
var $container3 = $('.container3').highlight({
selector: '.trigger5',
mode: 'click',
classes: 'blue',
target: '.target5'
})
tape('$().highlight() container click', check($container3, '.trigger5', 'click', '.target5.blue'))
tape('$().highlight() container click', check($container3, '.trigger5', 'click', '.target5'))
</script>
</body>
</html>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment