@ruc-lib/knob 3.1.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,595 @@
1
+ import * as i3 from '@angular/material/icon';
2
+ import { MatIconModule } from '@angular/material/icon';
3
+ import * as i2 from '@angular/material/button';
4
+ import { MatButtonModule } from '@angular/material/button';
5
+ import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import * as i1 from '@angular/common';
7
+ import { CommonModule } from '@angular/common';
8
+ import * as i0 from '@angular/core';
9
+ import { EventEmitter, forwardRef, HostListener, Input, Output, ViewChild, Component } from '@angular/core';
10
+
11
+ const DefaultKnobConfig = {
12
+ min: 0,
13
+ max: 100,
14
+ step: 1,
15
+ size: 150,
16
+ valueColor: '',
17
+ strokeBackground: 'lightblue',
18
+ progressBackground: 'blue', // 'green' | ['green'] | ['red', 'blue', 'green', 'black', 'orange'],
19
+ strokeWidth: 15,
20
+ valueSize: 20,
21
+ valueWeight: 'normal',
22
+ showHandle: true,
23
+ handleBackground: 'lightblue',
24
+ handleBorderColor: 'blue',
25
+ handleBorderWidth: 4,
26
+ roundedCorner: true,
27
+ valuePrefix: '',
28
+ valueSuffix: '',
29
+ readOnly: false,
30
+ disabled: false,
31
+ enableTooltip: false,
32
+ animateOnHover: false,
33
+ isRangeMode: false,
34
+ rangeStartValue: 25,
35
+ rangeEndValue: 75,
36
+ showButtons: false,
37
+ knobType: 'arc' // 'horizontal' | 'vertical' | 'arc'
38
+ };
39
+ const DEFAULT_LABELS = {
40
+ incrementButton: 'Increment Value',
41
+ decrementButton: 'Decrement Value'
42
+ };
43
+
44
+ /* eslint-disable @typescript-eslint/no-empty-function */
45
+ /* eslint-disable @typescript-eslint/no-unused-vars */
46
+ /* eslint-disable @typescript-eslint/no-inferrable-types */
47
+ class RuclibKnobComponent {
48
+ constructor(cdr) {
49
+ this.cdr = cdr;
50
+ this.rucEvent = new EventEmitter();
51
+ this.customTheme = '';
52
+ this.activeHandle = '';
53
+ this.value = 0;
54
+ this.dragging = false;
55
+ this.centerX = 0;
56
+ this.centerY = 0;
57
+ this.radius = 0;
58
+ this.startAngle = 210;
59
+ this.endAngle = 510;
60
+ this.arcLength = 300;
61
+ this.changeColorAfter = 0;
62
+ this.tooltipX = 0;
63
+ this.tooltipY = 0;
64
+ this.showTooltip = false;
65
+ this.hovering = false;
66
+ this.config = DefaultKnobConfig;
67
+ this.onTouched = () => { };
68
+ this.onChange = (value) => { };
69
+ }
70
+ /**
71
+ * handling form control binding to write value
72
+ * @param val
73
+ */
74
+ writeValue(val) {
75
+ this.value = val;
76
+ }
77
+ /**
78
+ * registering onChange method to use as form control
79
+ * @param fn
80
+ */
81
+ registerOnChange(fn) {
82
+ this.onChange = fn;
83
+ }
84
+ /**
85
+ * registering onTouch method to use as form control
86
+ * @param fn
87
+ */
88
+ registerOnTouched(fn) {
89
+ this.onTouched = fn;
90
+ }
91
+ /**
92
+ * registering disabled state
93
+ * @param isDisabled
94
+ */
95
+ setDisabledState(isDisabled) {
96
+ this.config.disabled = isDisabled;
97
+ }
98
+ /**
99
+ * handling input data changes
100
+ * updating default config with user provided config
101
+ * @param changes
102
+ */
103
+ ngOnChanges(changes) {
104
+ if (changes && changes['rucInputData'] && changes['rucInputData'].currentValue) {
105
+ this.config = { ...this.config, ...changes['rucInputData'].currentValue };
106
+ }
107
+ }
108
+ /**
109
+ * handling change on component initilization
110
+ */
111
+ ngOnInit() {
112
+ this.adjustDefaultValue();
113
+ if (this.config.knobType != 'arc') {
114
+ this.config.isRangeMode = false;
115
+ this.config.enableTooltip = false;
116
+ }
117
+ if (Array.isArray(this.config.progressBackground)) {
118
+ this.changeColorAfter = Math.round(100 / this.config.progressBackground.length);
119
+ }
120
+ }
121
+ /**
122
+ * handling change after view initilization
123
+ */
124
+ ngAfterViewInit() {
125
+ this.centerX = this.config.size / 2;
126
+ this.centerY = this.config.size / 2;
127
+ this.radius = this.config.size / 2 - 20;
128
+ this.bgArcRef?.nativeElement.setAttribute('d', this.describeArc(this.centerX, this.centerY, this.radius, this.startAngle, this.endAngle));
129
+ this.updateArc();
130
+ this.cdr.detectChanges();
131
+ }
132
+ /**
133
+ * handling change when dragin on svg
134
+ * @returns
135
+ */
136
+ startDrag() {
137
+ if (this.config.disabled || this.config.readOnly)
138
+ return;
139
+ this.dragging = true;
140
+ this.showTooltip = true;
141
+ this.rucEvent.emit({ eventName: 'dragStart', eventOutput: { value: this.getEventOutput() } });
142
+ }
143
+ /**
144
+ * rounding value to increment or decrement based on provide config value for step
145
+ * @param value
146
+ * @returns
147
+ */
148
+ roundToStep(value) {
149
+ const stepped = Math.round((value - this.config.min) / this.config.step) * this.config.step + this.config.min;
150
+ return this.clamp(stepped, this.config.min, this.config.max);
151
+ }
152
+ /**
153
+ * adjusting default value within min & max value when its provide out of range
154
+ */
155
+ adjustDefaultValue() {
156
+ if (this.value < this.config.min) {
157
+ this.value = this.config.min;
158
+ }
159
+ if (this.value > this.config.max) {
160
+ this.value = this.config.max;
161
+ }
162
+ if (this.config.isRangeMode) {
163
+ if (this.config.rangeStartValue < this.config.min || this.config.rangeStartValue > this.config.max) {
164
+ this.config.rangeStartValue = this.config.min;
165
+ }
166
+ if (this.config.rangeEndValue > this.config.max || this.config.rangeEndValue < this.config.min) {
167
+ this.config.rangeEndValue = this.config.max;
168
+ }
169
+ }
170
+ this.updateArc();
171
+ }
172
+ /**
173
+ * handle changes on mouseUp and touchEnd event
174
+ */
175
+ stopDrag() {
176
+ this.dragging = false;
177
+ this.showTooltip = false;
178
+ this.rucEvent.emit({ eventName: 'dragEnd', eventOutput: { value: this.getEventOutput() } });
179
+ }
180
+ /**
181
+ * handle changes on mouseMove and touch event
182
+ * @param event
183
+ * @returns
184
+ */
185
+ onMove(event) {
186
+ if (this.config.disabled || this.config.readOnly || !this.dragging)
187
+ return;
188
+ event.preventDefault();
189
+ this.setProgressFromEvent(event);
190
+ }
191
+ /**
192
+ * handling change on main svg click
193
+ * @param event
194
+ * @returns
195
+ */
196
+ onSvgClick(event) {
197
+ if (this.config.disabled || this.config.readOnly || this.config.isRangeMode)
198
+ return;
199
+ this.setProgressFromEvent(event);
200
+ }
201
+ /**
202
+ * get ref of active svg element for different type of knobs
203
+ * @returns
204
+ */
205
+ getTargetSvg() {
206
+ if (this.config.knobType === 'horizontal') {
207
+ return this.horizontalLineRef.nativeElement.closest('svg');
208
+ }
209
+ else if (this.config.knobType === 'vertical') {
210
+ return this.verticalLineRef.nativeElement.closest('svg');
211
+ }
212
+ return this.bgArcRef.nativeElement.closest('svg');
213
+ }
214
+ /**
215
+ * updating progrees value while dragging the handle on stroke bar
216
+ * @param e
217
+ * @returns
218
+ */
219
+ setProgressFromEvent(e) {
220
+ const svg = this.getTargetSvg();
221
+ if (!svg) {
222
+ return;
223
+ }
224
+ const rect = svg.getBoundingClientRect();
225
+ const clientX = (e instanceof TouchEvent) ? e.touches[0].clientX : e.clientX;
226
+ const clientY = (e instanceof TouchEvent) ? e.touches[0].clientY : e.clientY;
227
+ const x = clientX - rect.left;
228
+ const y = clientY - rect.top;
229
+ let rawPercent;
230
+ if (this.config.knobType === 'horizontal') {
231
+ const usableWidth = this.config.size - 2 * this.config.strokeWidth;
232
+ rawPercent = ((x - this.config.strokeWidth) / usableWidth) * 100;
233
+ }
234
+ else if (this.config.knobType === 'vertical') {
235
+ const usableHeight = this.config.size - 2 * this.config.strokeWidth;
236
+ rawPercent = (1 - (y - this.config.strokeWidth) / usableHeight) * 100;
237
+ }
238
+ else {
239
+ const angle = this.getAngleFromPoint(x, y);
240
+ if (angle === null)
241
+ return;
242
+ rawPercent = ((angle - this.startAngle) / this.arcLength) * 100;
243
+ }
244
+ const clampedPercent = this.clamp(rawPercent, 0, 100);
245
+ let absolutePercent = this.config.min + (clampedPercent / 100) * (this.config.max - this.config.min);
246
+ absolutePercent = this.roundToStep(absolutePercent);
247
+ if (this.config.isRangeMode) {
248
+ if (this.activeHandle === 'start') {
249
+ if (absolutePercent > this.config.rangeEndValue) {
250
+ absolutePercent = this.config.rangeEndValue;
251
+ }
252
+ this.config.rangeStartValue = absolutePercent;
253
+ }
254
+ else {
255
+ if (absolutePercent < this.config.rangeStartValue) {
256
+ absolutePercent = this.config.rangeStartValue;
257
+ }
258
+ this.config.rangeEndValue = absolutePercent;
259
+ }
260
+ this.rucEvent.emit({ eventName: 'valueChange', eventOutput: { start: this.config.rangeStartValue, end: this.config.rangeEndValue } });
261
+ }
262
+ else {
263
+ this.value = absolutePercent;
264
+ this.rucEvent.emit({ eventName: 'valueChange', eventOutput: this.value });
265
+ }
266
+ this.updateArc();
267
+ this.onChange(this.value);
268
+ this.onTouched();
269
+ }
270
+ /**
271
+ * updating svg progress based on value changes
272
+ * @returns
273
+ */
274
+ updateArc() {
275
+ if (this.config.knobType !== 'arc') {
276
+ return;
277
+ }
278
+ const scaled = (this.value - this.config.min) / (this.config.max - this.config.min);
279
+ const angle = this.startAngle + scaled * this.arcLength;
280
+ const path = this.describeArc(this.centerX, this.centerY, this.radius, this.startAngle, angle);
281
+ this.progressArcRef?.nativeElement.setAttribute('d', path);
282
+ const pos = this.polarToCartesian(this.centerX, this.centerY, this.radius, angle);
283
+ this.handleRef?.nativeElement.setAttribute('cx', pos.x.toString());
284
+ this.handleRef?.nativeElement.setAttribute('cy', pos.y.toString());
285
+ // for tooltip
286
+ const angleRad = (angle - 90) * Math.PI / 180;
287
+ const tooltipRadius = this.radius + this.config.strokeWidth / 2 + 10;
288
+ this.tooltipX = this.centerX + tooltipRadius * Math.cos(angleRad);
289
+ this.tooltipY = this.centerY + tooltipRadius * Math.sin(angleRad);
290
+ }
291
+ /**
292
+ * return maximum value out of min & max range
293
+ * @param val
294
+ * @param min
295
+ * @param max
296
+ * @returns
297
+ */
298
+ clamp(val, min, max) {
299
+ return Math.max(min, Math.min(max, val));
300
+ }
301
+ /**
302
+ * getting calulated point from polar coordinates to cartesian coordinates
303
+ * @param cx
304
+ * @param cy
305
+ * @param r
306
+ * @param angleDeg
307
+ * @returns
308
+ */
309
+ polarToCartesian(cx, cy, r, angleDeg) {
310
+ const angleRad = (angleDeg - 90) * Math.PI / 180;
311
+ return {
312
+ x: cx + r * Math.cos(angleRad),
313
+ y: cy + r * Math.sin(angleRad)
314
+ };
315
+ }
316
+ /**
317
+ * getting radius for arc handle based on stroke width
318
+ * @returns
319
+ */
320
+ getRadius() {
321
+ return this.config.strokeWidth ? (this.config.strokeWidth / 2) - ((this.config.handleBorderWidth ?? 0) / 2) : 4;
322
+ }
323
+ /**
324
+ * getting svg box size based on different knob shapes
325
+ * @returns
326
+ */
327
+ getSvgViewBoxSize() {
328
+ let width = this.config.size, height = this.config.size;
329
+ if (this.config.knobType === 'horizontal') {
330
+ height = this.config.strokeWidth + 40;
331
+ }
332
+ if (this.config.knobType === 'vertical') {
333
+ height = this.config.size / 4 + 5;
334
+ width = this.config.strokeWidth + 40;
335
+ }
336
+ return '0 0 ' + width + ' ' + height;
337
+ }
338
+ /**
339
+ * geeting dynamic bg color for progress stroke based on provide config for "progressBackground"
340
+ */
341
+ get progressColor() {
342
+ if (typeof this.config.progressBackground === 'string') {
343
+ return this.config.progressBackground;
344
+ }
345
+ else if (this.config.progressBackground?.length == 1) {
346
+ return this.config.progressBackground[0];
347
+ }
348
+ else if (this.config.progressBackground.length > 1) {
349
+ return this.config.progressBackground[Math.ceil(this.value / this.changeColorAfter) - 1];
350
+ }
351
+ else {
352
+ return 'green';
353
+ }
354
+ }
355
+ /**
356
+ * getting coordinates for arc based on provided inputs
357
+ * @param cx
358
+ * @param cy
359
+ * @param r
360
+ * @param start
361
+ * @param end
362
+ * @returns
363
+ */
364
+ describeArc(cx, cy, r, start, end) {
365
+ const startPos = this.polarToCartesian(cx, cy, r, end);
366
+ const endPos = this.polarToCartesian(cx, cy, r, start);
367
+ const largeArc = end - start <= 180 ? 0 : 1;
368
+ return [
369
+ "M", startPos.x, startPos.y,
370
+ "A", r, r, 0, largeArc, 0, endPos.x, endPos.y
371
+ ].join(" ");
372
+ }
373
+ /**
374
+ * getting calculated angle for arc progress based on provided input
375
+ * @param x
376
+ * @param y
377
+ * @returns
378
+ */
379
+ getAngleFromPoint(x, y) {
380
+ const dx = x - this.centerX;
381
+ if (dx === 0) {
382
+ return null;
383
+ }
384
+ const dy = y - this.centerY;
385
+ let angle = Math.atan2(dy, dx) * 180 / Math.PI + 90;
386
+ if (angle < 0)
387
+ angle += 360;
388
+ const normalizedStart = this.startAngle % 360;
389
+ let delta = angle - normalizedStart;
390
+ if (delta < 0)
391
+ delta += 360;
392
+ if (delta > this.arcLength)
393
+ return null;
394
+ return this.startAngle + delta;
395
+ }
396
+ /**
397
+ * increment value on click on button
398
+ * @returns
399
+ */
400
+ increment() {
401
+ if (this.config.disabled || this.config.readOnly)
402
+ return;
403
+ this.value = this.clamp((this.value + this.config.step), this.config.min, this.config.max);
404
+ this.updateArc();
405
+ this.onChange(this.value);
406
+ this.onTouched();
407
+ }
408
+ /**
409
+ * decrement value on click on button
410
+ * @returns
411
+ */
412
+ decrement() {
413
+ if (this.config.disabled || this.config.readOnly)
414
+ return;
415
+ this.value = this.clamp((this.value - this.config.step), this.config.min, this.config.max);
416
+ this.updateArc();
417
+ this.onChange(this.value);
418
+ this.onTouched();
419
+ }
420
+ /**
421
+ * change value using arrow keys for accessibility
422
+ * @param event
423
+ * @returns
424
+ */
425
+ onKeyDown(event) {
426
+ if (this.config.readOnly || this.config.disabled)
427
+ return;
428
+ if (event.key === 'ArrowRight' || event.key === 'ArrowUp') {
429
+ this.increment();
430
+ event.preventDefault();
431
+ }
432
+ else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') {
433
+ this.decrement();
434
+ event.preventDefault();
435
+ }
436
+ }
437
+ /**
438
+ * geeting arc coordinated for range selection mode
439
+ * @returns
440
+ */
441
+ getRangeArcPath() {
442
+ const startAngle = this.startAngle + (this.config.rangeStartValue / (this.config.max - this.config.min)) * (this.endAngle - this.startAngle);
443
+ const endAngle = this.startAngle + (this.config.rangeEndValue / (this.config.max - this.config.min)) * (this.endAngle - this.startAngle);
444
+ return this.describeArc(this.centerX, this.centerY, this.radius, startAngle, endAngle);
445
+ }
446
+ /**
447
+ * handling mousedown when range mode is enabled
448
+ * @param event
449
+ * @param handleType
450
+ * @returns
451
+ */
452
+ onHandleMouseDown(event, handleType) {
453
+ if (this.config.disabled || this.config.readOnly)
454
+ return;
455
+ this.activeHandle = handleType;
456
+ this.startDrag();
457
+ }
458
+ /**
459
+ * getting x & y to update handle position when dragging
460
+ * @param value
461
+ * @returns
462
+ */
463
+ getHandlePosition(value) {
464
+ const scaled = (value - this.config.min) / (this.config.max - this.config.min);
465
+ const angle = this.startAngle + scaled * this.arcLength;
466
+ const pos = this.polarToCartesian(this.centerX, this.centerY, this.radius, angle);
467
+ return pos;
468
+ }
469
+ /**
470
+ * geeting handle position for horizontal line
471
+ * @param value
472
+ * @returns
473
+ */
474
+ getHorizontalHandleX(value) {
475
+ const usableWidth = this.config.size - 2 * this.config.strokeWidth;
476
+ const ratio = (value - this.config.min) / (this.config.max - this.config.min);
477
+ return this.config.strokeWidth + usableWidth * ratio;
478
+ }
479
+ /**
480
+ * geeting start position for horizontal line
481
+ * @param value
482
+ * @returns
483
+ */
484
+ getHorizontalLineStartX() {
485
+ return this.getHorizontalHandleX(this.config.isRangeMode ? this.config.rangeStartValue : this.config.min);
486
+ }
487
+ /**
488
+ * geeting end position for horizontal line
489
+ * @param value
490
+ * @returns
491
+ */
492
+ getHorizontalLineEndX() {
493
+ return this.getHorizontalHandleX(this.config.isRangeMode ? this.config.rangeEndValue : this.value);
494
+ }
495
+ /**
496
+ * geeting handle position for vertical line
497
+ * @param value
498
+ * @returns
499
+ */
500
+ getVerticalHandleY(value) {
501
+ const usableHeight = this.config.size - 2 * this.config.strokeWidth;
502
+ const ratio = 1 - (value - this.config.min) / (this.config.max - this.config.min);
503
+ return this.config.strokeWidth + usableHeight * ratio;
504
+ }
505
+ /**
506
+ * geeting start position for vertical line
507
+ * @param value
508
+ * @returns
509
+ */
510
+ getVerticalLineStartY() {
511
+ return this.getVerticalHandleY(this.config.isRangeMode ? this.config.rangeEndValue : this.value);
512
+ }
513
+ /**
514
+ * get output to be emitted based on range mode
515
+ * @returns
516
+ */
517
+ getEventOutput() {
518
+ if (this.config.isRangeMode) {
519
+ return { start: this.config.rangeStartValue, end: this.config.rangeEndValue };
520
+ }
521
+ return this.value;
522
+ }
523
+ /**
524
+ * get correct page label from object
525
+ * @param labelName
526
+ * @returns string
527
+ */
528
+ getLabel(labelName) {
529
+ return DEFAULT_LABELS[labelName] || '';
530
+ }
531
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibKnobComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
532
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: RuclibKnobComponent, isStandalone: true, selector: "uxp-ruclib-knob", inputs: { customTheme: "customTheme", rucInputData: "rucInputData" }, outputs: { rucEvent: "rucEvent" }, host: { listeners: { "window:mouseup": "stopDrag()", "window:touchend": "stopDrag()", "window:mousemove": "onMove($event)", "window:touchmove": "onMove($event)" } }, providers: [
533
+ {
534
+ provide: NG_VALUE_ACCESSOR,
535
+ useExisting: forwardRef(() => RuclibKnobComponent),
536
+ multi: true
537
+ }
538
+ ], viewQueries: [{ propertyName: "bgArcRef", first: true, predicate: ["bgArc"], descendants: true }, { propertyName: "progressArcRef", first: true, predicate: ["progressArc"], descendants: true }, { propertyName: "handleRef", first: true, predicate: ["handle"], descendants: true }, { propertyName: "horizontalLineRef", first: true, predicate: ["horizontalLine"], descendants: true }, { propertyName: "verticalLineRef", first: true, predicate: ["verticalLine"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"knob-container {{customTheme}}\" [style.width.px]=\"config.size\">\r\n <svg [ngClass]=\"{ 'hover-animate': config.animateOnHover }\" [attr.viewBox]=\"getSvgViewBoxSize()\"\r\n (click)=\"onSvgClick($event)\" [style.cursor]=\"(config.readOnly || config.disabled) ? 'not-allowed' : 'pointer'\"\r\n [class.disabled]=\"config.disabled\" [class.read-only]=\"config.readOnly\"\r\n (mouseenter)=\"showTooltip = true; hovering=true; rucEvent.emit({eventName: 'hover'})\"\r\n (mouseleave)=\"showTooltip = false; hovering=false\" (focus)=\"rucEvent.emit({eventName: 'focus'})\"\r\n (blur)=\"rucEvent.emit({eventName: 'blur'})\" (keydown)=\"onKeyDown($event)\" [attr.role]=\"'slider'\"\r\n [attr.aria-valuemin]=\"config.min\" [attr.aria-valuemax]=\"config.max\" [attr.aria-valuenow]=\"value\"\r\n [attr.aria-disabled]=\"config.disabled\">\r\n @switch (config.knobType) {\r\n <!-- arc knob -->\r\n @case ('arc') {\r\n <ng-container>\r\n <!-- glow effect -->\r\n <defs>\r\n <filter id=\"glow\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\r\n <feDropShadow dx=\"0\" dy=\"0\" stdDeviation=\"4\" [attr.flood-color]=\"config.strokeBackground\"\r\n flood-opacity=\"0.75\" />\r\n </filter>\r\n </defs>\r\n <!-- arc main stroke -->\r\n <path #bgArc fill=\"none\" class=\"main-path\" [attr.stroke]=\"config.strokeBackground\"\r\n [attr.stroke-width]=\"config.strokeWidth\" [attr.stroke-linecap]=\"config.roundedCorner ? 'round' : ''\" />\r\n <!-- arc progress stroke - single handle -->\r\n @if (!config.isRangeMode) {\r\n <path #progressArc fill=\"none\" class=\"progress-path\" [attr.stroke]=\"progressColor\"\r\n [attr.stroke-width]=\"config.strokeWidth\" [attr.stroke-linecap]=\"config.roundedCorner ? 'round' : ''\" />\r\n }\r\n <!-- arc - single handle -->\r\n @if (!config.isRangeMode) {\r\n <circle #handle class=\"handle\" [attr.r]=\"getRadius()\"\r\n [attr.fill]=\"config.showHandle ? config.handleBackground : 'transparent'\"\r\n [attr.stroke-width]=\"config.showHandle ? config.handleBorderWidth : 0\"\r\n [attr.stroke]=\"config.showHandle ? config.handleBorderColor : 'transparent'\" (mousedown)=\"startDrag()\"\r\n (touchstart)=\"startDrag()\" />\r\n }\r\n <!-- arc progress stroke - dual handle for range -->\r\n @if (config.isRangeMode) {\r\n <path [attr.d]=\"getRangeArcPath()\" [attr.stroke]=\"progressColor\"\r\n [attr.stroke-width]=\"config.strokeWidth\" fill=\"none\" stroke-linecap=\"round\" />\r\n }\r\n <!-- arc dual handle - start -->\r\n @if (config.isRangeMode) {\r\n <circle [attr.cx]=\"getHandlePosition(config.rangeStartValue).x\"\r\n [attr.cy]=\"getHandlePosition(config.rangeStartValue).y\" [attr.r]=\"getRadius()\"\r\n [attr.fill]=\"config.handleBackground\" [attr.stroke-width]=\"config.handleBorderWidth\"\r\n [attr.stroke]=\"config.handleBorderColor\" (mousedown)=\"onHandleMouseDown($event, 'start')\" />\r\n }\r\n <!-- arc dual handle - end -->\r\n @if (config.isRangeMode) {\r\n <circle [attr.cx]=\"getHandlePosition(config.rangeEndValue).x\"\r\n [attr.cy]=\"getHandlePosition(config.rangeEndValue).y\" [attr.r]=\"getRadius()\"\r\n [attr.fill]=\"config.handleBackground\" [attr.stroke-width]=\"config.handleBorderWidth\"\r\n [attr.stroke]=\"config.handleBorderColor\" (mousedown)=\"onHandleMouseDown($event, 'end')\" />\r\n }\r\n </ng-container>\r\n }\r\n <!-- horizontal line -->\r\n @case ('horizontal') {\r\n <ng-container>\r\n <line #horizontalLine [attr.x1]=\"config.strokeWidth\" [attr.x2]=\"config.size\" [attr.y1]=\"config.strokeWidth + 10\"\r\n [attr.y2]=\"config.strokeWidth + 10\" [attr.stroke]=\"config.strokeBackground\"\r\n [attr.stroke-width]=\"config.strokeWidth\" [attr.line-cap]=\"config.roundedCorner ? 'round' : ''\" />\r\n <!-- progress for horizontal line -->\r\n <line [attr.x1]=\"getHorizontalLineStartX()\" [attr.x2]=\"getHorizontalLineEndX()\"\r\n [attr.y1]=\"config.strokeWidth + 10\" [attr.y2]=\"config.strokeWidth + 10\" [attr.stroke]=\"progressColor\"\r\n [attr.stroke-width]=\"config.strokeWidth\" />\r\n <!-- handle for horizontal line -->\r\n @if (!config.isRangeMode) {\r\n <rect [attr.x]=\"getHorizontalHandleX(value)\"\r\n [attr.y]=\"config.strokeWidth + 10 - getRadius()-1\" [attr.width]=\"config.strokeWidth\"\r\n [attr.height]=\"config.strokeWidth\"\r\n [attr.fill]=\"config.handleBackground ? config.handleBackground : progressColor\" (mousedown)=\"startDrag()\"\r\n (touchstart)=\"startDrag()\" />\r\n }\r\n </ng-container>\r\n }\r\n <!-- vertical line -->\r\n @case ('vertical') {\r\n <ng-container>\r\n <line #verticalLine [attr.y1]=\"config.strokeWidth/4\" [attr.y2]=\"config.size/4\" [attr.x1]=\"config.strokeWidth + 10\"\r\n [attr.x2]=\"config.strokeWidth + 10\" [attr.stroke]=\"config.strokeBackground\"\r\n [attr.stroke-width]=\"config.strokeWidth/4\" [attr.line-cap]=\"config.roundedCorner ? 'round' : ''\" />\r\n <!-- progress for vertical line -->\r\n <line [attr.y1]=\"getVerticalLineStartY()/4\" [attr.y2]=\"config.size/4\" [attr.x1]=\"config.strokeWidth + 10\"\r\n [attr.x2]=\"config.strokeWidth + 10\" [attr.stroke]=\"progressColor\" [attr.stroke-width]=\"config.strokeWidth/4\" />\r\n <!-- Handle for vertical line -->\r\n @if (!config.isRangeMode) {\r\n <rect [attr.y]=\"getVerticalHandleY(value)/4\" [attr.x]=\"config.strokeWidth + 7.5\"\r\n [attr.width]=\"config.strokeWidth/4\" [attr.height]=\"config.strokeWidth/4\"\r\n [attr.fill]=\"config.handleBackground ? config.handleBackground : progressColor\" (mousedown)=\"startDrag()\"\r\n (touchstart)=\"startDrag()\" />\r\n }\r\n </ng-container>\r\n }\r\n }\r\n </svg>\r\n\r\n <!-- tooltip -->\r\n @if (config.enableTooltip && !config.isRangeMode) {\r\n <div class=\"tooltip\" [class.show]=\"showTooltip\"\r\n [style.left.px]=\"tooltipX\" [style.top.px]=\"tooltipY\">\r\n {{ value}}\r\n </div>\r\n }\r\n\r\n <!-- progress value -->\r\n <div class=\"progress-value {{config.knobType}}\" [style.maxWidth.px]=\"config.size * 2 - 50\"\r\n [style.color]=\"config.valueColor\" [style.fontSize.px]=\"config.valueSize\" [style.fontWeight]=\"config.valueWeight\"\r\n [style.cursor]=\"(config.readOnly || config.disabled) ? 'not-allowed' : ''\" [class.disabled]=\"config.disabled\"\r\n [class.read-only]=\"config.readOnly\" title=\"{{config.valuePrefix +''+value+''+config.valueSuffix}}\">\r\n @if (!config.isRangeMode) {\r\n <span class=\"value-prefix\">{{config.valuePrefix}}</span>\r\n <span class=\"value\">{{ value }}</span>\r\n <span class=\"value-suffix\">{{config.valueSuffix}}</span>\r\n }\r\n @if (config.isRangeMode) {\r\n <span class=\"value\">{{config.rangeStartValue}} : {{config.rangeEndValue}}</span>\r\n }\r\n </div>\r\n\r\n <!-- increment-decrement button -->\r\n @if (!config.isRangeMode && config.showButtons) {\r\n <div class=\"arc-buttons\">\r\n <button mat-mini-fab color=\"secondary\" (click)=\"decrement()\" [disabled]=\"config.disabled || config.readOnly\" (keydown)=\"onKeyDown($event)\"\r\n [attr.aria-label]=\"getLabel('decrementButton')\">\r\n <mat-icon>remove</mat-icon>\r\n </button>\r\n <button mat-mini-fab color=\"secondary\" (click)=\"increment()\" [disabled]=\"config.disabled || config.readOnly\" (keydown)=\"onKeyDown($event)\"\r\n [attr.aria-label]=\"getLabel('incrementButton')\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".knob-container{position:relative;padding-bottom:10px}svg{width:100%;height:100%;outline:none}.progress-value{left:50%;font-size:24px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;transform:translate(-50%,-50%);-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%)}.progress-value.arc{position:absolute;top:45%}.progress-value.horizontal,.progress-value.vertical{position:relative;display:flex;justify-content:center;align-items:center;padding-top:20px}.handle{cursor:pointer}.disabled{opacity:.6;pointer-events:none}.read-only{opacity:.8}.arc-buttons{display:flex;justify-content:center;gap:1rem;margin-top:-10px}.arc-buttons button{padding:6px;width:35px;height:35px;font-size:1rem;cursor:pointer;border:none;border-radius:4px;transition:background .2s ease;box-shadow:0 0 1px 1px #ddd!important}::ng-deep .mat-mdc-mini-fab:not(.mdc-fab--extended) .mdc-fab__ripple{border-radius:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;-ms-border-radius:0!important;-o-border-radius:0!important}.arc-buttons button:disabled{opacity:.6;cursor:not-allowed}.tooltip{position:absolute;background:#333;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;pointer-events:none;white-space:nowrap;transform:translate(-50%,-100%);opacity:0;transition:opacity .3s ease}.tooltip.show{opacity:1}.main-path{transition:all 1s ease;-webkit-transition:all 1s ease;-moz-transition:all 1s ease;-ms-transition:all 1s ease;-o-transition:all 1s ease}.hover-animate:hover .main-path{filter:url(#glow);-webkit-filter:url(#glow)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] }); }
539
+ }
540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: RuclibKnobComponent, decorators: [{
541
+ type: Component,
542
+ args: [{ selector: 'uxp-ruclib-knob', imports: [CommonModule,
543
+ FormsModule,
544
+ ReactiveFormsModule,
545
+ MatButtonModule,
546
+ MatIconModule], providers: [
547
+ {
548
+ provide: NG_VALUE_ACCESSOR,
549
+ useExisting: forwardRef(() => RuclibKnobComponent),
550
+ multi: true
551
+ }
552
+ ], template: "<div class=\"knob-container {{customTheme}}\" [style.width.px]=\"config.size\">\r\n <svg [ngClass]=\"{ 'hover-animate': config.animateOnHover }\" [attr.viewBox]=\"getSvgViewBoxSize()\"\r\n (click)=\"onSvgClick($event)\" [style.cursor]=\"(config.readOnly || config.disabled) ? 'not-allowed' : 'pointer'\"\r\n [class.disabled]=\"config.disabled\" [class.read-only]=\"config.readOnly\"\r\n (mouseenter)=\"showTooltip = true; hovering=true; rucEvent.emit({eventName: 'hover'})\"\r\n (mouseleave)=\"showTooltip = false; hovering=false\" (focus)=\"rucEvent.emit({eventName: 'focus'})\"\r\n (blur)=\"rucEvent.emit({eventName: 'blur'})\" (keydown)=\"onKeyDown($event)\" [attr.role]=\"'slider'\"\r\n [attr.aria-valuemin]=\"config.min\" [attr.aria-valuemax]=\"config.max\" [attr.aria-valuenow]=\"value\"\r\n [attr.aria-disabled]=\"config.disabled\">\r\n @switch (config.knobType) {\r\n <!-- arc knob -->\r\n @case ('arc') {\r\n <ng-container>\r\n <!-- glow effect -->\r\n <defs>\r\n <filter id=\"glow\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\r\n <feDropShadow dx=\"0\" dy=\"0\" stdDeviation=\"4\" [attr.flood-color]=\"config.strokeBackground\"\r\n flood-opacity=\"0.75\" />\r\n </filter>\r\n </defs>\r\n <!-- arc main stroke -->\r\n <path #bgArc fill=\"none\" class=\"main-path\" [attr.stroke]=\"config.strokeBackground\"\r\n [attr.stroke-width]=\"config.strokeWidth\" [attr.stroke-linecap]=\"config.roundedCorner ? 'round' : ''\" />\r\n <!-- arc progress stroke - single handle -->\r\n @if (!config.isRangeMode) {\r\n <path #progressArc fill=\"none\" class=\"progress-path\" [attr.stroke]=\"progressColor\"\r\n [attr.stroke-width]=\"config.strokeWidth\" [attr.stroke-linecap]=\"config.roundedCorner ? 'round' : ''\" />\r\n }\r\n <!-- arc - single handle -->\r\n @if (!config.isRangeMode) {\r\n <circle #handle class=\"handle\" [attr.r]=\"getRadius()\"\r\n [attr.fill]=\"config.showHandle ? config.handleBackground : 'transparent'\"\r\n [attr.stroke-width]=\"config.showHandle ? config.handleBorderWidth : 0\"\r\n [attr.stroke]=\"config.showHandle ? config.handleBorderColor : 'transparent'\" (mousedown)=\"startDrag()\"\r\n (touchstart)=\"startDrag()\" />\r\n }\r\n <!-- arc progress stroke - dual handle for range -->\r\n @if (config.isRangeMode) {\r\n <path [attr.d]=\"getRangeArcPath()\" [attr.stroke]=\"progressColor\"\r\n [attr.stroke-width]=\"config.strokeWidth\" fill=\"none\" stroke-linecap=\"round\" />\r\n }\r\n <!-- arc dual handle - start -->\r\n @if (config.isRangeMode) {\r\n <circle [attr.cx]=\"getHandlePosition(config.rangeStartValue).x\"\r\n [attr.cy]=\"getHandlePosition(config.rangeStartValue).y\" [attr.r]=\"getRadius()\"\r\n [attr.fill]=\"config.handleBackground\" [attr.stroke-width]=\"config.handleBorderWidth\"\r\n [attr.stroke]=\"config.handleBorderColor\" (mousedown)=\"onHandleMouseDown($event, 'start')\" />\r\n }\r\n <!-- arc dual handle - end -->\r\n @if (config.isRangeMode) {\r\n <circle [attr.cx]=\"getHandlePosition(config.rangeEndValue).x\"\r\n [attr.cy]=\"getHandlePosition(config.rangeEndValue).y\" [attr.r]=\"getRadius()\"\r\n [attr.fill]=\"config.handleBackground\" [attr.stroke-width]=\"config.handleBorderWidth\"\r\n [attr.stroke]=\"config.handleBorderColor\" (mousedown)=\"onHandleMouseDown($event, 'end')\" />\r\n }\r\n </ng-container>\r\n }\r\n <!-- horizontal line -->\r\n @case ('horizontal') {\r\n <ng-container>\r\n <line #horizontalLine [attr.x1]=\"config.strokeWidth\" [attr.x2]=\"config.size\" [attr.y1]=\"config.strokeWidth + 10\"\r\n [attr.y2]=\"config.strokeWidth + 10\" [attr.stroke]=\"config.strokeBackground\"\r\n [attr.stroke-width]=\"config.strokeWidth\" [attr.line-cap]=\"config.roundedCorner ? 'round' : ''\" />\r\n <!-- progress for horizontal line -->\r\n <line [attr.x1]=\"getHorizontalLineStartX()\" [attr.x2]=\"getHorizontalLineEndX()\"\r\n [attr.y1]=\"config.strokeWidth + 10\" [attr.y2]=\"config.strokeWidth + 10\" [attr.stroke]=\"progressColor\"\r\n [attr.stroke-width]=\"config.strokeWidth\" />\r\n <!-- handle for horizontal line -->\r\n @if (!config.isRangeMode) {\r\n <rect [attr.x]=\"getHorizontalHandleX(value)\"\r\n [attr.y]=\"config.strokeWidth + 10 - getRadius()-1\" [attr.width]=\"config.strokeWidth\"\r\n [attr.height]=\"config.strokeWidth\"\r\n [attr.fill]=\"config.handleBackground ? config.handleBackground : progressColor\" (mousedown)=\"startDrag()\"\r\n (touchstart)=\"startDrag()\" />\r\n }\r\n </ng-container>\r\n }\r\n <!-- vertical line -->\r\n @case ('vertical') {\r\n <ng-container>\r\n <line #verticalLine [attr.y1]=\"config.strokeWidth/4\" [attr.y2]=\"config.size/4\" [attr.x1]=\"config.strokeWidth + 10\"\r\n [attr.x2]=\"config.strokeWidth + 10\" [attr.stroke]=\"config.strokeBackground\"\r\n [attr.stroke-width]=\"config.strokeWidth/4\" [attr.line-cap]=\"config.roundedCorner ? 'round' : ''\" />\r\n <!-- progress for vertical line -->\r\n <line [attr.y1]=\"getVerticalLineStartY()/4\" [attr.y2]=\"config.size/4\" [attr.x1]=\"config.strokeWidth + 10\"\r\n [attr.x2]=\"config.strokeWidth + 10\" [attr.stroke]=\"progressColor\" [attr.stroke-width]=\"config.strokeWidth/4\" />\r\n <!-- Handle for vertical line -->\r\n @if (!config.isRangeMode) {\r\n <rect [attr.y]=\"getVerticalHandleY(value)/4\" [attr.x]=\"config.strokeWidth + 7.5\"\r\n [attr.width]=\"config.strokeWidth/4\" [attr.height]=\"config.strokeWidth/4\"\r\n [attr.fill]=\"config.handleBackground ? config.handleBackground : progressColor\" (mousedown)=\"startDrag()\"\r\n (touchstart)=\"startDrag()\" />\r\n }\r\n </ng-container>\r\n }\r\n }\r\n </svg>\r\n\r\n <!-- tooltip -->\r\n @if (config.enableTooltip && !config.isRangeMode) {\r\n <div class=\"tooltip\" [class.show]=\"showTooltip\"\r\n [style.left.px]=\"tooltipX\" [style.top.px]=\"tooltipY\">\r\n {{ value}}\r\n </div>\r\n }\r\n\r\n <!-- progress value -->\r\n <div class=\"progress-value {{config.knobType}}\" [style.maxWidth.px]=\"config.size * 2 - 50\"\r\n [style.color]=\"config.valueColor\" [style.fontSize.px]=\"config.valueSize\" [style.fontWeight]=\"config.valueWeight\"\r\n [style.cursor]=\"(config.readOnly || config.disabled) ? 'not-allowed' : ''\" [class.disabled]=\"config.disabled\"\r\n [class.read-only]=\"config.readOnly\" title=\"{{config.valuePrefix +''+value+''+config.valueSuffix}}\">\r\n @if (!config.isRangeMode) {\r\n <span class=\"value-prefix\">{{config.valuePrefix}}</span>\r\n <span class=\"value\">{{ value }}</span>\r\n <span class=\"value-suffix\">{{config.valueSuffix}}</span>\r\n }\r\n @if (config.isRangeMode) {\r\n <span class=\"value\">{{config.rangeStartValue}} : {{config.rangeEndValue}}</span>\r\n }\r\n </div>\r\n\r\n <!-- increment-decrement button -->\r\n @if (!config.isRangeMode && config.showButtons) {\r\n <div class=\"arc-buttons\">\r\n <button mat-mini-fab color=\"secondary\" (click)=\"decrement()\" [disabled]=\"config.disabled || config.readOnly\" (keydown)=\"onKeyDown($event)\"\r\n [attr.aria-label]=\"getLabel('decrementButton')\">\r\n <mat-icon>remove</mat-icon>\r\n </button>\r\n <button mat-mini-fab color=\"secondary\" (click)=\"increment()\" [disabled]=\"config.disabled || config.readOnly\" (keydown)=\"onKeyDown($event)\"\r\n [attr.aria-label]=\"getLabel('incrementButton')\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".knob-container{position:relative;padding-bottom:10px}svg{width:100%;height:100%;outline:none}.progress-value{left:50%;font-size:24px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;transform:translate(-50%,-50%);-webkit-transform:translate(-50%,-50%);-moz-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);-o-transform:translate(-50%,-50%)}.progress-value.arc{position:absolute;top:45%}.progress-value.horizontal,.progress-value.vertical{position:relative;display:flex;justify-content:center;align-items:center;padding-top:20px}.handle{cursor:pointer}.disabled{opacity:.6;pointer-events:none}.read-only{opacity:.8}.arc-buttons{display:flex;justify-content:center;gap:1rem;margin-top:-10px}.arc-buttons button{padding:6px;width:35px;height:35px;font-size:1rem;cursor:pointer;border:none;border-radius:4px;transition:background .2s ease;box-shadow:0 0 1px 1px #ddd!important}::ng-deep .mat-mdc-mini-fab:not(.mdc-fab--extended) .mdc-fab__ripple{border-radius:0!important;-webkit-border-radius:0!important;-moz-border-radius:0!important;-ms-border-radius:0!important;-o-border-radius:0!important}.arc-buttons button:disabled{opacity:.6;cursor:not-allowed}.tooltip{position:absolute;background:#333;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;pointer-events:none;white-space:nowrap;transform:translate(-50%,-100%);opacity:0;transition:opacity .3s ease}.tooltip.show{opacity:1}.main-path{transition:all 1s ease;-webkit-transition:all 1s ease;-moz-transition:all 1s ease;-ms-transition:all 1s ease;-o-transition:all 1s ease}.hover-animate:hover .main-path{filter:url(#glow);-webkit-filter:url(#glow)}\n"] }]
553
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { bgArcRef: [{
554
+ type: ViewChild,
555
+ args: ['bgArc']
556
+ }], progressArcRef: [{
557
+ type: ViewChild,
558
+ args: ['progressArc']
559
+ }], handleRef: [{
560
+ type: ViewChild,
561
+ args: ['handle']
562
+ }], horizontalLineRef: [{
563
+ type: ViewChild,
564
+ args: ['horizontalLine']
565
+ }], verticalLineRef: [{
566
+ type: ViewChild,
567
+ args: ['verticalLine']
568
+ }], rucEvent: [{
569
+ type: Output
570
+ }], customTheme: [{
571
+ type: Input
572
+ }], rucInputData: [{
573
+ type: Input
574
+ }], stopDrag: [{
575
+ type: HostListener,
576
+ args: ['window:mouseup']
577
+ }, {
578
+ type: HostListener,
579
+ args: ['window:touchend']
580
+ }], onMove: [{
581
+ type: HostListener,
582
+ args: ['window:mousemove', ['$event']]
583
+ }, {
584
+ type: HostListener,
585
+ args: ['window:touchmove', ['$event']]
586
+ }] } });
587
+
588
+ ;
589
+
590
+ /**
591
+ * Generated bundle index. Do not edit.
592
+ */
593
+
594
+ export { RuclibKnobComponent };
595
+ //# sourceMappingURL=ruc-lib-knob.mjs.map