Add form support to `Knob`

pull/6632/head
Mert Sincan 2024-10-21 14:25:46 +01:00
parent 112d3eca77
commit 387f86d0bb
4 changed files with 52 additions and 28 deletions

View File

@ -1,24 +1,16 @@
<script> <script>
import BaseComponent from '@primevue/core/basecomponent';
import { $dt } from '@primeuix/styled'; import { $dt } from '@primeuix/styled';
import BaseEditableHolder from '@primevue/core/baseeditableholder';
import KnobStyle from 'primevue/knob/style'; import KnobStyle from 'primevue/knob/style';
export default { export default {
name: 'BaseKnob', name: 'BaseKnob',
extends: BaseComponent, extends: BaseEditableHolder,
props: { props: {
modelValue: {
type: Number,
default: null
},
size: { size: {
type: Number, type: Number,
default: 100 default: 100
}, },
disabled: {
type: Boolean,
default: false
},
readonly: { readonly: {
type: Boolean, type: Boolean,
default: false default: false

View File

@ -119,11 +119,24 @@ export interface KnobProps {
* Value of the component. * Value of the component.
*/ */
modelValue?: number | undefined; modelValue?: number | undefined;
/**
* The default value for the input when not controlled by `modelValue`.
*/
defaultValue?: any;
/**
* The name attribute for the element, typically used in form submissions.
*/
name?: string | undefined;
/** /**
* Size of the component in pixels. * Size of the component in pixels.
* @defaultValue 100 * @defaultValue 100
*/ */
size?: number | undefined; size?: number | undefined;
/**
* When present, it specifies that the component should have invalid state style.
* @defaultValue false
*/
invalid?: boolean | undefined;
/** /**
* When present, it specifies that the component should be disabled. * When present, it specifies that the component should be disabled.
* @defaultValue false * @defaultValue false
@ -192,6 +205,10 @@ export interface KnobProps {
* Used to define a string that labels the element. * Used to define a string that labels the element.
*/ */
ariaLabel?: string | undefined; ariaLabel?: string | undefined;
/**
* Form control object, typically used for handling validation and form state.
*/
formControl?: Record<string, any> | undefined;
/** /**
* It generates scoped CSS variables using design tokens for the component. * It generates scoped CSS variables using design tokens for the component.
*/ */
@ -224,12 +241,17 @@ export interface KnobSlots {}
export interface KnobEmitsOptions { export interface KnobEmitsOptions {
/** /**
* Emitted when the value changes. * Emitted when the value changes.
* @param {number} event - New value. * @param {number} value - New value.
*/ */
'update:modelValue'(value: number): void; 'update:modelValue'(value: number): void;
/**
* Emitted when the value changes in uncontrolled mode.
* @param {number} value - New value.
*/
'value-change'(value: number): void;
/** /**
* Callback to invoke when the value changes. * Callback to invoke when the value changes.
* @param {number} event - New value * @param {number} value - New value
*/ */
change(value: number): void; change(value: number): void;
} }

View File

@ -8,10 +8,11 @@
:tabindex="readonly || disabled ? -1 : tabindex" :tabindex="readonly || disabled ? -1 : tabindex"
:aria-valuemin="min" :aria-valuemin="min"
:aria-valuemax="max" :aria-valuemax="max"
:aria-valuenow="modelValue" :aria-valuenow="d_value"
:aria-labelledby="ariaLabelledby" :aria-labelledby="ariaLabelledby"
:aria-label="ariaLabel" :aria-label="ariaLabel"
@click="onClick" @click="onClick"
@blur="onBlur"
@keydown="onKeyDown" @keydown="onKeyDown"
@mousedown="onMouseDown" @mousedown="onMouseDown"
@mouseup="onMouseUp" @mouseup="onMouseUp"
@ -36,7 +37,7 @@ export default {
name: 'Knob', name: 'Knob',
extends: BaseKnob, extends: BaseKnob,
inheritAttrs: false, inheritAttrs: false,
emits: ['update:modelValue', 'change'], emits: ['change'],
data() { data() {
return { return {
radius: 40, radius: 40,
@ -64,13 +65,13 @@ export default {
let newValue = Math.round((mappedValue - this.min) / this.step) * this.step + this.min; let newValue = Math.round((mappedValue - this.min) / this.step) * this.step + this.min;
this.$emit('update:modelValue', newValue); this.updateValue(newValue);
this.$emit('change', newValue); this.$emit('change', newValue);
}, },
updateModelValue(newValue) { updateModelValue(newValue) {
if (newValue > this.max) this.$emit('update:modelValue', this.max); if (newValue > this.max) this.updateValue(this.max);
else if (newValue < this.min) this.$emit('update:modelValue', this.min); else if (newValue < this.min) this.updateValue(this.min);
else this.$emit('update:modelValue', newValue); else this.updateValue(newValue);
}, },
mapRange(x, inMin, inMax, outMin, outMax) { mapRange(x, inMin, inMax, outMin, outMax) {
return ((x - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin; return ((x - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
@ -80,6 +81,9 @@ export default {
this.updateValue(event.offsetX, event.offsetY); this.updateValue(event.offsetX, event.offsetY);
} }
}, },
onBlur(event) {
this.formField.onBlur?.(event);
},
onMouseDown(event) { onMouseDown(event) {
if (!this.disabled && !this.readonly) { if (!this.disabled && !this.readonly) {
window.addEventListener('mousemove', this.onMouseMove); window.addEventListener('mousemove', this.onMouseMove);
@ -131,7 +135,7 @@ export default {
case 'ArrowUp': { case 'ArrowUp': {
event.preventDefault(); event.preventDefault();
this.updateModelValue(this.modelValue + this.step); this.updateModelValue(this.d_value + this.step);
break; break;
} }
@ -139,31 +143,31 @@ export default {
case 'ArrowDown': { case 'ArrowDown': {
event.preventDefault(); event.preventDefault();
this.updateModelValue(this.modelValue - this.step); this.updateModelValue(this.d_value - this.step);
break; break;
} }
case 'Home': { case 'Home': {
event.preventDefault(); event.preventDefault();
this.$emit('update:modelValue', this.min); this.updateValue(this.min);
break; break;
} }
case 'End': { case 'End': {
event.preventDefault(); event.preventDefault();
this.$emit('update:modelValue', this.max); this.updateValue(this.max);
break; break;
} }
case 'PageUp': { case 'PageUp': {
event.preventDefault(); event.preventDefault();
this.updateModelValue(this.modelValue + 10); this.updateModelValue(this.d_value + 10);
break; break;
} }
case 'PageDown': { case 'PageDown': {
event.preventDefault(); event.preventDefault();
this.updateModelValue(this.modelValue - 10); this.updateModelValue(this.d_value - 10);
break; break;
} }
} }
@ -182,7 +186,7 @@ export default {
else return this.mapRange(0, this.min, this.max, this.minRadians, this.maxRadians); else return this.mapRange(0, this.min, this.max, this.minRadians, this.maxRadians);
}, },
valueRadians() { valueRadians() {
return this.mapRange(this.modelValue, this.min, this.max, this.minRadians, this.maxRadians); return this.mapRange(this.d_value, this.min, this.max, this.minRadians, this.maxRadians);
}, },
minX() { minX() {
return this.midX + Math.cos(this.minRadians) * this.radius; return this.midX + Math.cos(this.minRadians) * this.radius;
@ -216,9 +220,9 @@ export default {
}, },
valueToDisplay() { valueToDisplay() {
if (typeof this.valueTemplate === 'string') { if (typeof this.valueTemplate === 'string') {
return this.valueTemplate.replace(/{value}/g, this.modelValue); return this.valueTemplate.replace(/{value}/g, this.d_value);
} else { } else {
return this.valueTemplate(this.modelValue); return this.valueTemplate(this.d_value);
} }
} }
} }

View File

@ -37,7 +37,13 @@ const theme = ({ dt }) => `
`; `;
const classes = { const classes = {
root: ({ props }) => ['p-knob p-component', { 'p-disabled': props.disabled }], root: ({ instance, props }) => [
'p-knob p-component',
{
'p-disabled': props.disabled,
'p-invalid': instance.$invalid
}
],
range: 'p-knob-range', range: 'p-knob-range',
value: 'p-knob-value', value: 'p-knob-value',
text: 'p-knob-text' text: 'p-knob-text'