Skip to content

Commit

Permalink
create range slider with histogram #96 (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
UcDust authored Sep 30, 2024
1 parent 569ae08 commit 525c729
Show file tree
Hide file tree
Showing 9 changed files with 1,086 additions and 3 deletions.
1 change: 1 addition & 0 deletions brand-app/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import "./pages/page-ucdlib-sils-search-redirect";
import "./pages/page-ucdlib-md";
import "./pages/page-ucdlib-header";
import "./pages/page-ucdlib-primary-nav.js";
import "./pages/page-ucdlib-range-slider.js";

// non-component js.
import {PageWidthController} from '../elements/utils/controllers';
Expand Down
1 change: 1 addition & 0 deletions brand-app/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import './ucdlib-theme-brand-app.js';

// IMPORT APP PAGES HERE
import "./pages/page-ucdlib-range-slider.js";

// guides
import "./pages/page-overview.js";
Expand Down
30 changes: 30 additions & 0 deletions brand-app/pages/page-ucdlib-range-slider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { LitElement } from 'lit';
import {render, styles} from "./page-ucdlib-range-slider.tpl.js";

import {Mixin, MainDomElement} from '../../elements/utils/mixins';
import {BrandedPageElement, MdElement} from "../utils/index.js";

export default class PageUcdlibRangeSlider extends Mixin(LitElement)
.with(MainDomElement, BrandedPageElement, MdElement) {

static get properties() {
return {
};
}

static get styles() {
return styles();
}

constructor() {
super();
this.render = render.bind(this);
}

_onRangeSliderChange(e) {
// do something with e.detail, ie {min: 1971, max: 2010, includeUnknown: true}
}

}

customElements.define('page-ucdlib-range-slider', PageUcdlibRangeSlider);
93 changes: 93 additions & 0 deletions brand-app/pages/page-ucdlib-range-slider.tpl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { html, css } from 'lit';
import '@ucd-lib/theme-elements/ucdlib/ucdlib-range-slider/ucdlib-range-slider';

export function styles() {
const elementStyles = css`
:host {
display: block;
}
`;

return [elementStyles];
}

export function render() {
return html`
${this.pageTitle("Range slider with histogram")}
<p>The <code>ucdlib-range-slider</code> element is a range slider component with start / end inputs and an optional histogram.</p>
<h2>Using the component</h2>
${this.examplePanel(html`
<div style="max-width: 350px">
<ucdlib-range-slider
@range-slider-change="${this._onRangeSliderChange}"
.data=${[
{ stat : 2020, value : 42 },
{ stat : 2021, value : 52 },
{ stat : 2022, value : 32 },
{ stat : 2023, value : 62 },
{ stat : 2024, value : 22 },
{ stat : 2025, value : 24 },
{ stat : 2026, value : 27 },
{ stat : 2027, value : 75 },
{ stat : 2028, value : 72 },
{ stat : 2029, value : 88 },
{ stat : 2030, value : 11 },
{ stat : 2031, value : 77 },
{ stat : 2032, value : 45 },
{ stat : 2033, value : 60 }
]}>
</ucdlib-range-slider>
</div>
`)}
<p>
Note, the <code>.data</code> attribute is expecting an array of objects containing a
<code>stat</code> and a <code>value</code>, for instance,
<code>[{ stat : 2020, value : 42 }, ..., { stat : 2033, value : 60 }]</code>.
</p>
<p>
Also, if there are less than 5 stats to display, the histogram will not be shown. The max number of bins (bars) is 50,
so if there are more than 50, multiple stats will be grouped together.
</p>
<p>
When the slider min / max values are changed, or when the "Include unknown/unspecified" checkbox is changed,
an event is triggered to <code>range-slider-change</code>.
The event details will include <code>min</code>, <code>max</code>, and <code>includeUnknown</code>.
</p>
<h2>Customizing the component</h2>
<p>It's possible to override the colors of the histogram and slider, as well as to hide the labels under the min / max ends of the slider.</p>
${this.examplePanel(html`
<div style="max-width: 350px">
<ucdlib-range-slider
@range-slider-change="${this._onRangeSliderChange}"
.data=${[
{ stat : 1971, value : 42 },
{ stat : 1972, value : 52 },
{ stat : 1973, value : 32 },
{ stat : 1974, value : 62 },
{ stat : 1975, value : 22 },
{ stat : 1976, value : 24 },
{ stat : 1977, value : 27 },
{ stat : 1978, value : 75 },
{ stat : 1979, value : 72 },
{ stat : 1980, value : 88 },
{ stat : 1981, value : 11 },
{ stat : 1982, value : 77 },
{ stat : 1983, value : 45 },
{ stat : 1984, value : 60 }
]}
light-color="#FFF4D2"
medium-color="#FFDF80"
dark-color="#FFBF00"
hide-slider-labels>
</ucdlib-range-slider>
`)}
`;
}
218 changes: 218 additions & 0 deletions brand-app/pages/ucdlib-range-slider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
### Functions

<dl>
<dt><a href="#connectedCallback">connectedCallback()</a></dt>
<dd><p>setup our window mouse listeners, fire first render</p>
</dd>
<dt><a href="#disconnectedCallback">disconnectedCallback()</a></dt>
<dd><p>remove our window mouse listeners</p>
</dd>
<dt><a href="#_onResize">_onResize(evt, reMerge)</a></dt>
<dd><p>when the window resizes, re-render the histogram</p>
</dd>
<dt><a href="#_updateHistogram">_updateHistogram(reMerge)</a></dt>
<dd><p>render histogram bins based on the min and max values from data received,
and the width of the rendered svg. smallest bin possible is 6px with 2px gaps,
and a max of 50 bins is possible.
otherwise data points will be merged to fit within the max bins.
if less than 5 bins, histogram will be hidden.</p>
</dd>
<dt><a href="#_updateHistogramColors">_updateHistogramColors()</a></dt>
<dd><p>update light/medium/dark bin colors in histogram</p>
</dd>
<dt><a href="#_valueToPx">_valueToPx(value)</a> ⇒ <code>Number</code></dt>
<dd><p>given a number line value, return px location relative
to the widget</p>
</dd>
<dt><a href="#_pxToValue">_pxToValue(px)</a> ⇒ <code>Number</code></dt>
<dd><p>given a px location, return number line value</p>
</dd>
<dt><a href="#_renderAsync">_renderAsync()</a></dt>
<dd><p>debounce render calls</p>
</dd>
<dt><a href="#_render">_render()</a></dt>
<dd><p>set top/left px values for buttons/slider</p>
</dd>
<dt><a href="#_onRangeSliderChange">_onRangeSliderChange()</a></dt>
<dd><p>moving of range slider has stopped</p>
</dd>
<dt><a href="#_onRangeNullChange">_onRangeNullChange()</a></dt>
<dd><p>bound to input checkbox</p>
</dd>
<dt><a href="#_onInputChange">_onInputChange()</a></dt>
<dd><p>bound to min/max number inputs</p>
</dd>
<dt><a href="#_isFilterApplied">_isFilterApplied()</a> ⇒ <code>Boolean</code></dt>
<dd><p>is there currenlty a filter set</p>
</dd>
<dt><a href="#_notifySelected">_notifySelected()</a></dt>
<dd><p>notify parent of selection change</p>
</dd>
<dt><a href="#reset">reset()</a></dt>
<dd><p>reset range filter</p>
</dd>
<dt><a href="#_onMoveStart">_onMoveStart(e)</a></dt>
<dd><p>bound to btns and center line. Fired when the user mouses
down on element indicating a move is starting</p>
</dd>
<dt><a href="#_onMove">_onMove(e)</a></dt>
<dd><p>bound to mousemove event on this element. Update min/max
values based on type of move that is happening ie min, max or range. Does
nothing if we are not moving.</p>
</dd>
<dt><a href="#_onMoveStop">_onMoveStop()</a></dt>
<dd><p>bound to mouseup/mouseout event on window. It&#39;s always best to bind
this to the window as a catch all. Resets all moving flags</p>
</dd>
</dl>

<a name="connectedCallback"></a>

### connectedCallback()
setup our window mouse listeners, fire first render

**Kind**: global function
<a name="disconnectedCallback"></a>

### disconnectedCallback()
remove our window mouse listeners

**Kind**: global function
<a name="_onResize"></a>

### \_onResize(evt, reMerge)
when the window resizes, re-render the histogram

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| evt | <code>Event</code> | |
| reMerge | <code>Boolean</code> | should we re-merge the data, typically when resizing window could cause difference |

<a name="_updateHistogram"></a>

### \_updateHistogram(reMerge)
render histogram bins based on the min and max values from data received,
and the width of the rendered svg. smallest bin possible is 6px with 2px gaps,
and a max of 50 bins is possible.
otherwise data points will be merged to fit within the max bins.
if less than 5 bins, histogram will be hidden.

**Kind**: global function

| Param | Type | Description |
| --- | --- | --- |
| reMerge | <code>Boolean</code> | should we re-merge the data, typically when resizing window could cause difference |

<a name="_updateHistogramColors"></a>

### \_updateHistogramColors()
update light/medium/dark bin colors in histogram

**Kind**: global function
<a name="_valueToPx"></a>

### \_valueToPx(value) ⇒ <code>Number</code>
given a number line value, return px location relative
to the widget

**Kind**: global function
**Returns**: <code>Number</code> - px location

| Param | Type | Description |
| --- | --- | --- |
| value | <code>Number</code> | number line value |

<a name="_pxToValue"></a>

### \_pxToValue(px) ⇒ <code>Number</code>
given a px location, return number line value

**Kind**: global function
**Returns**: <code>Number</code> - value

| Param | Type | Description |
| --- | --- | --- |
| px | <code>Number</code> | location |

<a name="_renderAsync"></a>

### \_renderAsync()
debounce render calls

**Kind**: global function
<a name="_render"></a>

### \_render()
set top/left px values for buttons/slider

**Kind**: global function
<a name="_onRangeSliderChange"></a>

### \_onRangeSliderChange()
moving of range slider has stopped

**Kind**: global function
<a name="_onRangeNullChange"></a>

### \_onRangeNullChange()
bound to input checkbox

**Kind**: global function
<a name="_onInputChange"></a>

### \_onInputChange()
bound to min/max number inputs

**Kind**: global function
<a name="_isFilterApplied"></a>

### \_isFilterApplied() ⇒ <code>Boolean</code>
is there currenlty a filter set

**Kind**: global function
<a name="_notifySelected"></a>

### \_notifySelected()
notify parent of selection change

**Kind**: global function
<a name="reset"></a>

### reset()
reset range filter

**Kind**: global function
<a name="_onMoveStart"></a>

### \_onMoveStart(e)
bound to btns and center line. Fired when the user mouses
down on element indicating a move is starting

**Kind**: global function

| Param | Type |
| --- | --- |
| e | <code>MouseEvent</code> |

<a name="_onMove"></a>

### \_onMove(e)
bound to mousemove event on this element. Update min/max
values based on type of move that is happening ie min, max or range. Does
nothing if we are not moving.

**Kind**: global function

| Param | Type |
| --- | --- |
| e | <code>MouseEvent</code> |

<a name="_onMoveStop"></a>

### \_onMoveStop()
bound to mouseup/mouseout event on window. It's always best to bind
this to the window as a catch all. Resets all moving flags

**Kind**: global function
3 changes: 1 addition & 2 deletions brand-app/ucdlib-theme-brand-app.tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ return html`
</ul>
<li><a href="#ucdlib-md">Markdown</a></li>
<li><a href="#ucdlib-author-profile">Author Profile</a></li>
<li><a href="#ucdlib-range-slider">Range Slider</a></li>
</ul>
<ul link-text="Brand Guides" href="#overview">
Expand All @@ -101,8 +102,6 @@ return html`
<li><a href="#fonts">Fonts</a></li>
<li><a href="#brand-buttons">Buttons</a></li>
<li><a href="#brand-selectors">Selectors</a></li>
</ul>
</ucd-theme-primary-nav>
Expand Down
Loading

0 comments on commit 525c729

Please sign in to comment.