Skip to content

Commit 7aa609d

Browse files
committed
chore(slider): add documentation
1 parent d0a700f commit 7aa609d

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

src/slider/slider.component.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,36 @@ import { fromEvent, Subscription } from "rxjs";
1313
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
1414

1515
/**
16+
* Used to select from ranges of values. [See here](https://www.carbondesignsystem.com/components/slider/usage) for usage information.
1617
*
18+
* The simpelist possible slider usage looks something like:
19+
* ```html
20+
* <ibm-slider></ibm-slider>
21+
* ```
22+
*
23+
* That will render a slider without labels or alternative value input. Labels can be provided by
24+
* elements with `[minLabel]` and `[maxLabel]` attributes, and an `input` (may use the `ibmInput` directive) can be supplied
25+
* for use as an alternative value field.
26+
*
27+
* ex:
28+
* ```html
29+
* <!-- full example -->
30+
* <ibm-slider>
31+
* <span minLabel>0GB</span>
32+
* <span maxLabel>100GB</span>
33+
* <input/>
34+
* </ibm-slider>
35+
* <!-- with just an input -->
36+
* <ibm-slider>
37+
* <input/>
38+
* </ibm-slider>
39+
* <!-- with just one label -->
40+
* <ibm-slider>
41+
* <span maxLabel>Maximum</span>
42+
* </ibm-slider>
43+
* ```
44+
*
45+
* Slider supports `NgModel` by default, as well as two way binding to the `value` input.
1746
*/
1847
@Component({
1948
selector: "ibm-slider",
@@ -63,11 +92,15 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
6392
]
6493
})
6594
export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
95+
/** Used to generate unique IDs */
6696
private static count = 0;
67-
97+
/** The lower bound of our range */
6898
@Input() min = 0;
99+
/** The upper bound of our range */
69100
@Input() max = 100;
101+
/** The interval for our range */
70102
@Input() step = 1;
103+
/** Set the inital value. Avliable for two way binding */
71104
@Input() set value(v) {
72105
if (v > this.max) {
73106
v = this.max;
@@ -91,8 +124,14 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
91124
get value() {
92125
return this._value;
93126
}
127+
/** Base ID for the slider. The min and max labels get IDs `${this.id}-bottom-range` and `${this.id}-top-range` respectivly */
94128
@Input() id = `slider-${Slider.count++}`;
129+
/** Value used to "multiply" the `step` when using arrow keys to select values */
95130
@Input() shiftMultiplier = 4;
131+
/** Disables the range visually and functionally */
132+
// TODO: implement disabled state
133+
@Input() disabled = false;
134+
/** Emits every time a new value is selected */
96135
@Output() valueChange: EventEmitter<number> = new EventEmitter();
97136
@HostBinding("class.bx--slider-container") hostClass = true;
98137
@ViewChild("thumb") thumb: ElementRef;
@@ -103,6 +142,7 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
103142
public topRangeId = `${this.id}-top-range`;
104143

105144
protected isMouseDown = false;
145+
/** Array of event subscriptions so we can batch unsubscribe in `ngOnDestroy` */
106146
protected eventSubscriptions: Array<Subscription> = [];
107147
protected slidAmount = 0;
108148
protected input;
@@ -111,9 +151,12 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
111151
constructor(protected elementRef: ElementRef) {}
112152

113153
ngAfterViewInit() {
154+
// bind mousemove and mouseup to the document so we don't have issues tracking the mouse
114155
this.eventSubscriptions.push(fromEvent(document, "mousemove").subscribe(this.onMouseMove.bind(this)));
115156
this.eventSubscriptions.push(fromEvent(document, "mouseup").subscribe(this.onMouseUp.bind(this)));
116157

158+
// ODO: ontouchstart/ontouchmove/ontouchend
159+
117160
// set up the optional input
118161
this.input = this.elementRef.nativeElement.querySelector("input:not([type=range])");
119162
if (this.input) {
@@ -122,42 +165,52 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
122165
this.input.classList.add("bx--text-input");
123166
this.input.setAttribute("aria-labelledby", `${this.bottomRangeId} ${this.topRangeId}`);
124167
this.input.value = this.value;
168+
// bind events on our optional input
125169
this.eventSubscriptions.push(fromEvent(this.input, "change").subscribe(this.onChange.bind(this)));
126170
this.eventSubscriptions.push(fromEvent(this.input, "focus").subscribe(this.onFocus.bind(this)));
127171
}
128172
}
129173

174+
/** Clean up our DOMEvent subscriptions */
130175
ngOnDestroy() {
131176
this.eventSubscriptions.forEach(subscription => {
132177
subscription.unsubscribe();
133178
});
134179
}
135180

181+
/** Send changes back to the model */
136182
propagateChange = (_: any) => { };
137183

184+
/** Register a change propagation function for `ControlValueAccessor` */
138185
registerOnChange(fn: any) {
139186
this.propagateChange = fn;
140187
}
141188

189+
/** Callback to notify the model when our input has been touched */
142190
onTouched: () => any = () => { };
143191

192+
/** Register a callback to notify when our input has been touched */
144193
registerOnTouched(fn: any) {
145194
this.onTouched = fn;
146195
}
147196

197+
/** Recives a value from the model */
148198
writeValue(v: any) {
149199
this.value = v;
150200
}
151201

202+
/** Returns the amount of "completeness" as a fraction of the total track width */
152203
getPercentComplete() {
153204
const trackWidth = this.track.nativeElement.getBoundingClientRect().width;
154205
return this.slidAmount / trackWidth;
155206
}
156207

208+
/** Helper function to return the CSS transform `scaleX` function */
157209
scaleX(complete) {
158210
return `scaleX(${complete})`;
159211
}
160212

213+
/** Converts a given px value to a "real" value in our range */
161214
convertToValue(pxAmount) {
162215
// basic concept borrowed from carbon-components
163216
// ref: https://github.com/IBM/carbon-components/blob/43bf3abdc2f8bdaa38aa84e0f733adde1e1e8894/src/components/slider/slider.js#L147-L151
@@ -168,6 +221,7 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
168221
return rounded + this.min;
169222
}
170223

224+
/** Converts a given "real" value to a px value we can update the view with */
171225
convertToPx(value) {
172226
const trackWidth = this.track.nativeElement.getBoundingClientRect().width;
173227
if (value >= this.max) {
@@ -181,28 +235,42 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
181235
return Math.round(trackWidth * (value / this.max));
182236
}
183237

238+
/**
239+
* Increments the value by the step value, or the step value multiplied by the `multiplier` argument.
240+
*
241+
* @argument multiplier Defaults to `1`, multiplied with the step value.
242+
*/
184243
incrementValue(multiplier = 1) {
185244
this.value = this.value + (this.step * multiplier);
186245
}
187246

247+
/**
248+
* Decrements the value by the step value, or the step value multiplied by the `multiplier` argument.
249+
*
250+
* @argument multiplier Defaults to `1`, multiplied with the step value.
251+
*/
188252
decrementValue(multiplier = 1) {
189253
this.value = this.value - (this.step * multiplier);
190254
}
191255

256+
/** Change handler for the optional input */
192257
onChange(event) {
193258
this.value = event.target.value;
194259
}
195260

261+
/** Handles clicks on the range track, and setting the value to it's "real" equivilent */
196262
onClick(event) {
197263
const trackLeft = this.track.nativeElement.getBoundingClientRect().left;
198264
this.value = this.convertToValue(event.clientX - trackLeft);
199265
console.log(event);
200266
}
201267

268+
/** Focus handler for the optional input */
202269
onFocus({target}) {
203270
target.select();
204271
}
205272

273+
/** Mouse move handler. Responsible for updating the value and visual selection based on mouse movement */
206274
onMouseMove(event) {
207275
if (this.isMouseDown) {
208276
const trackWidth = this.track.nativeElement.getBoundingClientRect().width;
@@ -217,16 +285,19 @@ export class Slider implements AfterViewInit, OnDestroy, ControlValueAccessor {
217285
}
218286
}
219287

288+
/** Enables the `onMouseMove` handler */
220289
onMouseDown(event) {
221290
event.preventDefault();
222291
this.thumb.nativeElement.focus();
223292
this.isMouseDown = true;
224293
}
225294

295+
/** Disables the `onMouseMove` handler */
226296
onMouseUp() {
227297
this.isMouseDown = false;
228298
}
229299

300+
/** Calls `incrementValue` for ArrowRight and ArrowUp, `decrementValue` for ArrowLeft and ArrowDown */
230301
onKeyDown(event: KeyboardEvent) {
231302
event.preventDefault();
232303
const multiplier = event.shiftKey ? this.shiftMultiplier : 1;

0 commit comments

Comments
 (0)