primeng
Version:
PrimeNG is an open source UI library for Angular featuring a rich set of 80+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeB
559 lines (549 loc) • 21.4 kB
JavaScript
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, forwardRef, EventEmitter, inject, numberAttribute, booleanAttribute, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Output, NgModule } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { $dt } from '@primeuix/styled';
import { SharedModule } from 'primeng/api';
import { BaseComponent } from 'primeng/basecomponent';
import { BaseStyle } from 'primeng/base';
const theme = ({ dt }) => `
.p-knob-range {
fill: none;
transition: stroke 0.1s ease-in;
}
.p-knob-value {
animation-name: p-knob-dash-frame;
animation-fill-mode: forwards;
fill: none;
}
.p-knob-text {
font-size: 1.3rem;
text-align: center;
}
.p-knob svg {
border-radius: 50%;
outline-color: transparent;
transition: background ${dt('knob.transition.duration')}, color ${dt('knob.transition.duration')}, outline-color ${dt('knob.transition.duration')}, box-shadow ${dt('knob.transition.duration')};
}
.p-knob svg:focus-visible {
box-shadow: ${dt('knob.focus.ring.shadow')};
outline: ${dt('knob.focus.ring.width')} ${dt('knob.focus.ring.style')} ${dt('knob.focus.ring.color')};
outline-offset: ${dt('knob.focus.ring.offset')};
}
p-knob-dash-frame {
100% {
stroke-dashoffset: 0;
}
}
`;
const classes = {
root: ({ props }) => ['p-knob p-component', { 'p-disabled': props.disabled }],
range: 'p-knob-range',
value: 'p-knob-value',
text: 'p-knob-text'
};
class KnobStyle extends BaseStyle {
name = 'knob';
theme = theme;
classes = classes;
static ɵfac = /*@__PURE__*/ (() => { let ɵKnobStyle_BaseFactory; return function KnobStyle_Factory(__ngFactoryType__) { return (ɵKnobStyle_BaseFactory || (ɵKnobStyle_BaseFactory = i0.ɵɵgetInheritedFactory(KnobStyle)))(__ngFactoryType__ || KnobStyle); }; })();
static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: KnobStyle, factory: KnobStyle.ɵfac });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(KnobStyle, [{
type: Injectable
}], null, null); })();
/**
*
* Knob is a form component to define number inputs with a dial.
*
* [Live Demo](https://www.primeng.org/knob/)
*
* @module knobstyle
*
*/
var KnobClasses;
(function (KnobClasses) {
/**
* Class name of the root element
*/
KnobClasses["root"] = "p-knob";
/**
* Class name of the range element
*/
KnobClasses["range"] = "p-knob-range";
/**
* Class name of the value element
*/
KnobClasses["value"] = "p-knob-value";
/**
* Class name of the text element
*/
KnobClasses["text"] = "p-knob-text";
})(KnobClasses || (KnobClasses = {}));
function Knob__svg_text_4_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵnamespaceSVG();
i0.ɵɵelementStart(0, "text", 5);
i0.ɵɵtext(1);
i0.ɵɵelementEnd();
} if (rf & 2) {
const ctx_r0 = i0.ɵɵnextContext();
i0.ɵɵattribute("x", 50)("y", 57)("fill", ctx_r0.textColor)("name", ctx_r0.name);
i0.ɵɵadvance();
i0.ɵɵtextInterpolate1(" ", ctx_r0.valueToDisplay(), " ");
} }
const KNOB_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Knob),
multi: true
};
/**
* Knob is a form component to define number inputs with a dial.
* @group Components
*/
class Knob extends BaseComponent {
/**
* Style class of the component.
* @group Props
*/
styleClass;
/**
* Inline style of the component.
* @group Props
*/
style;
/**
* Defines a string that labels the input for accessibility.
* @group Props
*/
ariaLabel;
/**
* Specifies one or more IDs in the DOM that labels the input field.
* @group Props
*/
ariaLabelledBy;
/**
* Index of the element in tabbing order.
* @group Props
*/
tabindex = 0;
/**
* Background of the value.
* @group Props
*/
valueColor = $dt('knob.value.background').variable;
/**
* Background color of the range.
* @group Props
*/
rangeColor = $dt('knob.range.background').variable;
/**
* Color of the value text.
* @group Props
*/
textColor = $dt('knob.text.color').variable;
/**
* Template string of the value.
* @group Props
*/
valueTemplate = '{value}';
/**
* Name of the input element.
* @group Props
*/
name;
/**
* Size of the component in pixels.
* @group Props
*/
size = 100;
/**
* Step factor to increment/decrement the value.
* @group Props
*/
step = 1;
/**
* Mininum boundary value.
* @group Props
*/
min = 0;
/**
* Maximum boundary value.
* @group Props
*/
max = 100;
/**
* Width of the knob stroke.
* @group Props
*/
strokeWidth = 14;
/**
* When present, it specifies that the component should be disabled.
* @group Props
*/
disabled;
/**
* Whether the show the value inside the knob.
* @group Props
*/
showValue = true;
/**
* When present, it specifies that the component value cannot be edited.
* @group Props
*/
readonly = false;
/**
* Callback to invoke on value change.
* @param {number} value - New value.
* @group Emits
*/
onChange = new EventEmitter();
radius = 40;
midX = 50;
midY = 50;
minRadians = (4 * Math.PI) / 3;
maxRadians = -Math.PI / 3;
value = 0;
windowMouseMoveListener;
windowMouseUpListener;
windowTouchMoveListener;
windowTouchEndListener;
onModelChange = () => { };
onModelTouched = () => { };
_componentStyle = inject(KnobStyle);
get containerClass() {
return {
'p-knob p-component': true,
'p-disabled': this.disabled
};
}
mapRange(x, inMin, inMax, outMin, outMax) {
return ((x - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
}
onClick(event) {
if (!this.disabled && !this.readonly) {
this.updateValue(event.offsetX, event.offsetY);
}
}
updateValue(offsetX, offsetY) {
let dx = offsetX - this.size / 2;
let dy = this.size / 2 - offsetY;
let angle = Math.atan2(dy, dx);
let start = -Math.PI / 2 - Math.PI / 6;
this.updateModel(angle, start);
}
updateModel(angle, start) {
let mappedValue;
if (angle > this.maxRadians)
mappedValue = this.mapRange(angle, this.minRadians, this.maxRadians, this.min, this.max);
else if (angle < start)
mappedValue = this.mapRange(angle + 2 * Math.PI, this.minRadians, this.maxRadians, this.min, this.max);
else
return;
let newValue = Math.round((mappedValue - this.min) / this.step) * this.step + this.min;
this.value = newValue;
this.onModelChange(this.value);
this.onChange.emit(this.value);
}
onMouseDown(event) {
if (!this.disabled && !this.readonly) {
const window = this.document.defaultView || 'window';
this.windowMouseMoveListener = this.renderer.listen(window, 'mousemove', this.onMouseMove.bind(this));
this.windowMouseUpListener = this.renderer.listen(window, 'mouseup', this.onMouseUp.bind(this));
event.preventDefault();
}
}
onMouseUp(event) {
if (!this.disabled && !this.readonly) {
if (this.windowMouseMoveListener) {
this.windowMouseMoveListener();
this.windowMouseUpListener = null;
}
if (this.windowMouseUpListener) {
this.windowMouseUpListener();
this.windowMouseMoveListener = null;
}
event.preventDefault();
}
}
onTouchStart(event) {
if (!this.disabled && !this.readonly) {
const window = this.document.defaultView || 'window';
this.windowTouchMoveListener = this.renderer.listen(window, 'touchmove', this.onTouchMove.bind(this));
this.windowTouchEndListener = this.renderer.listen(window, 'touchend', this.onTouchEnd.bind(this));
event.preventDefault();
}
}
onTouchEnd(event) {
if (!this.disabled && !this.readonly) {
if (this.windowTouchMoveListener) {
this.windowTouchMoveListener();
}
if (this.windowTouchEndListener) {
this.windowTouchEndListener();
}
this.windowTouchMoveListener = null;
this.windowTouchEndListener = null;
event.preventDefault();
}
}
onMouseMove(event) {
if (!this.disabled && !this.readonly) {
this.updateValue(event.offsetX, event.offsetY);
event.preventDefault();
}
}
onTouchMove(event) {
if (!this.disabled && !this.readonly && event instanceof TouchEvent && event.touches.length === 1) {
const rect = this.el.nativeElement.children[0].getBoundingClientRect();
const touch = event.targetTouches.item(0);
if (touch) {
const offsetX = touch.clientX - rect.left;
const offsetY = touch.clientY - rect.top;
this.updateValue(offsetX, offsetY);
}
}
}
updateModelValue(newValue) {
if (newValue > this.max)
this.value = this.max;
else if (newValue < this.min)
this.value = this.min;
else
this.value = newValue;
this.onModelChange(this.value);
this.onChange.emit(this.value);
}
onKeyDown(event) {
if (!this.disabled && !this.readonly) {
switch (event.code) {
case 'ArrowRight':
case 'ArrowUp': {
event.preventDefault();
this.updateModelValue(this._value + 1);
break;
}
case 'ArrowLeft':
case 'ArrowDown': {
event.preventDefault();
this.updateModelValue(this._value - 1);
break;
}
case 'Home': {
event.preventDefault();
this.updateModelValue(this.min);
break;
}
case 'End': {
event.preventDefault();
this.updateModelValue(this.max);
break;
}
case 'PageUp': {
event.preventDefault();
this.updateModelValue(this._value + 10);
break;
}
case 'PageDown': {
event.preventDefault();
this.updateModelValue(this._value - 10);
break;
}
}
}
}
writeValue(value) {
this.value = value;
this.cd.markForCheck();
}
registerOnChange(fn) {
this.onModelChange = fn;
}
registerOnTouched(fn) {
this.onModelTouched = fn;
}
setDisabledState(val) {
this.disabled = val;
this.cd.markForCheck();
}
rangePath() {
return `M ${this.minX()} ${this.minY()} A ${this.radius} ${this.radius} 0 1 1 ${this.maxX()} ${this.maxY()}`;
}
valuePath() {
return `M ${this.zeroX()} ${this.zeroY()} A ${this.radius} ${this.radius} 0 ${this.largeArc()} ${this.sweep()} ${this.valueX()} ${this.valueY()}`;
}
zeroRadians() {
if (this.min > 0 && this.max > 0)
return this.mapRange(this.min, this.min, this.max, this.minRadians, this.maxRadians);
else
return this.mapRange(0, this.min, this.max, this.minRadians, this.maxRadians);
}
valueRadians() {
return this.mapRange(this._value, this.min, this.max, this.minRadians, this.maxRadians);
}
minX() {
return this.midX + Math.cos(this.minRadians) * this.radius;
}
minY() {
return this.midY - Math.sin(this.minRadians) * this.radius;
}
maxX() {
return this.midX + Math.cos(this.maxRadians) * this.radius;
}
maxY() {
return this.midY - Math.sin(this.maxRadians) * this.radius;
}
zeroX() {
return this.midX + Math.cos(this.zeroRadians()) * this.radius;
}
zeroY() {
return this.midY - Math.sin(this.zeroRadians()) * this.radius;
}
valueX() {
return this.midX + Math.cos(this.valueRadians()) * this.radius;
}
valueY() {
return this.midY - Math.sin(this.valueRadians()) * this.radius;
}
largeArc() {
return Math.abs(this.zeroRadians() - this.valueRadians()) < Math.PI ? 0 : 1;
}
sweep() {
return this.valueRadians() > this.zeroRadians() ? 0 : 1;
}
valueToDisplay() {
return this.valueTemplate.replace('{value}', this._value.toString());
}
get _value() {
return this.value != null ? this.value : this.min;
}
static ɵfac = /*@__PURE__*/ (() => { let ɵKnob_BaseFactory; return function Knob_Factory(__ngFactoryType__) { return (ɵKnob_BaseFactory || (ɵKnob_BaseFactory = i0.ɵɵgetInheritedFactory(Knob)))(__ngFactoryType__ || Knob); }; })();
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: Knob, selectors: [["p-knob"]], inputs: { styleClass: "styleClass", style: "style", ariaLabel: "ariaLabel", ariaLabelledBy: "ariaLabelledBy", tabindex: [2, "tabindex", "tabindex", numberAttribute], valueColor: "valueColor", rangeColor: "rangeColor", textColor: "textColor", valueTemplate: "valueTemplate", name: "name", size: [2, "size", "size", numberAttribute], step: [2, "step", "step", numberAttribute], min: [2, "min", "min", numberAttribute], max: [2, "max", "max", numberAttribute], strokeWidth: [2, "strokeWidth", "strokeWidth", numberAttribute], disabled: [2, "disabled", "disabled", booleanAttribute], showValue: [2, "showValue", "showValue", booleanAttribute], readonly: [2, "readonly", "readonly", booleanAttribute] }, outputs: { onChange: "onChange" }, features: [i0.ɵɵProvidersFeature([KNOB_VALUE_ACCESSOR, KnobStyle]), i0.ɵɵInputTransformsFeature, i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 24, consts: [[3, "ngClass", "ngStyle"], ["viewBox", "0 0 100 100", "role", "slider", 3, "click", "keydown", "mousedown", "mouseup", "touchstart", "touchend"], [1, "p-knob-range"], [1, "p-knob-value"], ["text-anchor", "middle", "class", "p-knob-text", 4, "ngIf"], ["text-anchor", "middle", 1, "p-knob-text"]], template: function Knob_Template(rf, ctx) { if (rf & 1) {
i0.ɵɵelementStart(0, "div", 0);
i0.ɵɵnamespaceSVG();
i0.ɵɵelementStart(1, "svg", 1);
i0.ɵɵlistener("click", function Knob_Template_svg_click_1_listener($event) { return ctx.onClick($event); })("keydown", function Knob_Template_svg_keydown_1_listener($event) { return ctx.onKeyDown($event); })("mousedown", function Knob_Template_svg_mousedown_1_listener($event) { return ctx.onMouseDown($event); })("mouseup", function Knob_Template_svg_mouseup_1_listener($event) { return ctx.onMouseUp($event); })("touchstart", function Knob_Template_svg_touchstart_1_listener($event) { return ctx.onTouchStart($event); })("touchend", function Knob_Template_svg_touchend_1_listener($event) { return ctx.onTouchEnd($event); });
i0.ɵɵelement(2, "path", 2)(3, "path", 3);
i0.ɵɵtemplate(4, Knob__svg_text_4_Template, 2, 5, "text", 4);
i0.ɵɵelementEnd()();
} if (rf & 2) {
i0.ɵɵclassMap(ctx.styleClass);
i0.ɵɵproperty("ngClass", ctx.containerClass)("ngStyle", ctx.style);
i0.ɵɵattribute("data-pc-name", "knob")("data-pc-section", "root");
i0.ɵɵadvance();
i0.ɵɵstyleProp("width", ctx.size + "px")("height", ctx.size + "px");
i0.ɵɵattribute("aria-valuemin", ctx.min)("aria-valuemax", ctx.max)("aria-valuenow", ctx._value)("aria-labelledby", ctx.ariaLabelledBy)("aria-label", ctx.ariaLabel)("tabindex", ctx.readonly || ctx.disabled ? -1 : ctx.tabindex)("data-pc-section", "svg");
i0.ɵɵadvance();
i0.ɵɵattribute("d", ctx.rangePath())("stroke-width", ctx.strokeWidth)("stroke", ctx.rangeColor);
i0.ɵɵadvance();
i0.ɵɵattribute("d", ctx.valuePath())("stroke-width", ctx.strokeWidth)("stroke", ctx.valueColor);
i0.ɵɵadvance();
i0.ɵɵproperty("ngIf", ctx.showValue);
} }, dependencies: [CommonModule, i1.NgClass, i1.NgIf, i1.NgStyle, SharedModule], encapsulation: 2, changeDetection: 0 });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(Knob, [{
type: Component,
args: [{
selector: 'p-knob',
standalone: true,
imports: [CommonModule, SharedModule],
template: `
<div [ngClass]="containerClass" [class]="styleClass" [ngStyle]="style" [attr.data-pc-name]="'knob'" [attr.data-pc-section]="'root'">
<svg
viewBox="0 0 100 100"
role="slider"
[style.width]="size + 'px'"
[style.height]="size + 'px'"
(click)="onClick($event)"
(keydown)="onKeyDown($event)"
(mousedown)="onMouseDown($event)"
(mouseup)="onMouseUp($event)"
(touchstart)="onTouchStart($event)"
(touchend)="onTouchEnd($event)"
[attr.aria-valuemin]="min"
[attr.aria-valuemax]="max"
[attr.aria-valuenow]="_value"
[attr.aria-labelledby]="ariaLabelledBy"
[attr.aria-label]="ariaLabel"
[attr.tabindex]="readonly || disabled ? -1 : tabindex"
[attr.data-pc-section]="'svg'"
>
<path [attr.d]="rangePath()" [attr.stroke-width]="strokeWidth" [attr.stroke]="rangeColor" class="p-knob-range"></path>
<path [attr.d]="valuePath()" [attr.stroke-width]="strokeWidth" [attr.stroke]="valueColor" class="p-knob-value"></path>
<text *ngIf="showValue" [attr.x]="50" [attr.y]="57" text-anchor="middle" [attr.fill]="textColor" class="p-knob-text" [attr.name]="name">
{{ valueToDisplay() }}
</text>
</svg>
</div>
`,
providers: [KNOB_VALUE_ACCESSOR, KnobStyle],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
}]
}], null, { styleClass: [{
type: Input
}], style: [{
type: Input
}], ariaLabel: [{
type: Input
}], ariaLabelledBy: [{
type: Input
}], tabindex: [{
type: Input,
args: [{ transform: numberAttribute }]
}], valueColor: [{
type: Input
}], rangeColor: [{
type: Input
}], textColor: [{
type: Input
}], valueTemplate: [{
type: Input
}], name: [{
type: Input
}], size: [{
type: Input,
args: [{ transform: numberAttribute }]
}], step: [{
type: Input,
args: [{ transform: numberAttribute }]
}], min: [{
type: Input,
args: [{ transform: numberAttribute }]
}], max: [{
type: Input,
args: [{ transform: numberAttribute }]
}], strokeWidth: [{
type: Input,
args: [{ transform: numberAttribute }]
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], showValue: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], readonly: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], onChange: [{
type: Output
}] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(Knob, { className: "Knob", filePath: "knob.ts", lineNumber: 56 }); })();
class KnobModule {
static ɵfac = function KnobModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || KnobModule)(); };
static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: KnobModule });
static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [Knob, SharedModule, SharedModule] });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(KnobModule, [{
type: NgModule,
args: [{
imports: [Knob, SharedModule],
exports: [Knob, SharedModule]
}]
}], null, null); })();
(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(KnobModule, { imports: [Knob, SharedModule], exports: [Knob, SharedModule] }); })();
/**
* Generated bundle index. Do not edit.
*/
export { KNOB_VALUE_ACCESSOR, Knob, KnobClasses, KnobModule, KnobStyle };
//# sourceMappingURL=primeng-knob.mjs.map