diff --git a/packages/primevue/src/keyfilter/BaseKeyFilter.js b/packages/primevue/src/keyfilter/BaseKeyFilter.js new file mode 100644 index 000000000..e3f59ad47 --- /dev/null +++ b/packages/primevue/src/keyfilter/BaseKeyFilter.js @@ -0,0 +1,8 @@ +import BaseDirective from '@primevue/core/basedirective'; +import KeyFilterStyle from 'primevue/keyfilter/style'; + +const BaseKeyFilter = BaseDirective.extend({ + style: KeyFilterStyle +}); + +export default BaseKeyFilter; diff --git a/packages/primevue/src/keyfilter/KeyFilter.d.ts b/packages/primevue/src/keyfilter/KeyFilter.d.ts new file mode 100644 index 000000000..77700ee48 --- /dev/null +++ b/packages/primevue/src/keyfilter/KeyFilter.d.ts @@ -0,0 +1,153 @@ +/** + * + * KeyFilter is a built-in feature of InputText to restrict user input based on a regular expression. + * + * [Live Demo](https://primevue.org/keyfilter) + * + * @module keyfilter + * + */ +import type { DesignToken, PassThrough } from '@primevue/core'; +import type { DirectiveHooks } from '@primevue/core/basedirective'; +import type { PassThroughOptions } from 'primevue/passthrough'; +import { DirectiveBinding, ObjectDirective } from 'vue'; + +export declare type KeyFilterDirectivePassThroughOptionType = KeyFilterDirectivePassThroughAttributes | null | undefined; + +/** + * Defines options of KeyFilter. + */ +export interface KeyFilterOptions { + /** + * Sets the pattern for key filtering. + * @defaultValue null + */ + pattern?: RegExp | undefined; + /** + * When enabled, instead of blocking keys, input is validated internally to test against the regular expression. + * @defaultValue false + */ + validateOnly?: boolean; + /** + * It generates scoped CSS variables using design tokens for the component. + */ + dt?: DesignToken; + /** + * Used to pass attributes to DOM elements inside the component. + * @type {KeyFilterDirectivePassThroughOptions} + */ + pt?: PassThrough; + /** + * Used to configure passthrough(pt) options of the component. + * @type {PassThroughOptions} + */ + ptOptions?: PassThroughOptions; + /** + * When enabled, it removes component related styles in the core. + * @defaultValue false + */ + unstyled?: boolean; +} + +/** + * Custom passthrough(pt) options. + * @see {@link KeyFilterOptions.pt} + */ +export interface KeyFilterDirectivePassThroughOptions { + /** + * Used to pass attributes to the root's DOM element. + */ + root?: KeyFilterDirectivePassThroughOptionType; + /** + * Used to manage all lifecycle hooks. + * @see {@link BaseDirective.DirectiveHooks} + */ + hooks?: DirectiveHooks; +} + +/** + * Custom passthrough attributes for each DOM elements + */ +export interface KeyFilterDirectivePassThroughAttributes { + [key: string]: any; +} + +/** + * Defines modifiers of KeyFilter directive. + */ +export interface KeyFilterModifiers { + /** + * Positive integer pattern for KeyFilter directive. + * @defaultValue false + */ + pint?: RegExp | undefined; + /** + * Integer pattern for KeyFilter directive. + * @defaultValue false + */ + int?: RegExp | undefined; + /** + * Positive number pattern for KeyFilter directive. + * @defaultValue false + */ + pnum?: RegExp | undefined; + /** + * Money pattern for KeyFilter directive. + * @defaultValue false + */ + money?: RegExp | undefined; + /** + * Number pattern for KeyFilter directive. + * @defaultValue false + */ + num?: RegExp | undefined; + /** + * Hexadecimal pattern for KeyFilter directive. + * @defaultValue false + */ + hex?: RegExp | undefined; + /** + * Email pattern for KeyFilter directive. + * @defaultValue false + */ + email?: RegExp | undefined; + /** + * Alphabetic pattern for KeyFilter directive. + * @defaultValue false + */ + alpha?: RegExp | undefined; + /** + * Alphanumeric pattern for KeyFilter directive. + * @defaultValue false + */ + alphanum?: RegExp | undefined; +} + +/** + * Binding of KeyFilter directive. + */ +export interface KeyFilterDirectiveBinding extends Omit { + /** + * Value of the KeyFilter. + */ + value?: string | KeyFilterOptions | undefined; + /** + * Modifiers of the KeyFilter. + * @type {KeyFilterModifiers} + */ + modifiers?: KeyFilterModifiers | undefined; +} + +/** + * **PrimeVue - KeyFilter** + * + * _KeyFilter is a built-in feature of InputText to restrict user input based on a regular expression._ + * + * [Live Demo](https://www.primevue.org/keyfilter/) + * --- --- + * ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png) + * + */ +declare const KeyFilter: ObjectDirective; + +export default KeyFilter; diff --git a/packages/primevue/src/keyfilter/KeyFilter.js b/packages/primevue/src/keyfilter/KeyFilter.js new file mode 100644 index 000000000..6bb8ce24f --- /dev/null +++ b/packages/primevue/src/keyfilter/KeyFilter.js @@ -0,0 +1,128 @@ +import { isAttributeEquals } from '@primeuix/utils/dom'; +import BaseKeyFilter from './BaseKeyFilter'; + +const KeyFilter = BaseKeyFilter.extend('keyfilter', { + beforeMount(el, options) { + let target = this.getTarget(el); + + if (!target) return; + + target.$_pkeyfilterModifier = this.getModifiers(options); + + if (typeof options.value) { + target.$_pkeyfilterPattern = options.value?.pattern || options.value; + target.$_pkeyfilterValidateOnly = options.value?.validateOnly || false; + } + + this.bindEvents(target); + + target.setAttribute('data-pd-keyfilter', true); + }, + updated(el, options) { + let target = this.getTarget(el); + + if (!target) return; + + target.$_pkeyfilterModifier = this.getModifiers(options); + this.unbindEvents(el, options); + + if (typeof options.value) { + target.$_pkeyfilterPattern = options.value?.pattern || options.value; + target.$_pkeyfilterValidateOnly = options.value?.validateOnly || false; + } + + this.bindEvents(target); + }, + unmounted(el, options) { + this.unbindEvents(el, options); + }, + DEFAULT_PATTERNS: { + pint: /[\d]/, + int: /[\d\-]/, + pnum: /[\d\.]/, + money: /[\d\.\s,]/, + num: /[\d\-\.]/, + hex: /[0-9a-f]/i, + email: /[a-z0-9_\.\-@]/i, + alpha: /[a-z_]/i, + alphanum: /[a-z0-9_]/i + }, + methods: { + getTarget(el) { + return isAttributeEquals(el, 'data-pc-name', 'inputtext') || isAttributeEquals(el, 'data-pc-name', 'textarea') ? el : null; + }, + getModifiers(options) { + if (options.modifiers && Object.keys(options.modifiers).length) { + return Object.keys(options.modifiers)[Object.keys.length - 1]; + } + + return ''; + }, + getRegex(target) { + return target.$_pkeyfilterPattern ? target.$_pkeyfilterPattern : target.$_pkeyfilterModifier ? this.DEFAULT_PATTERNS[target.$_pkeyfilterModifier] : /./; + }, + bindEvents(el) { + el.$_keyfilterKeydownEvent = (event) => this.onKeydown(event, el); + el.$_keyfilterPasteEvent = (event) => this.onPaste(event, el); + + el.addEventListener('keypress', el.$_keyfilterKeydownEvent); + el.addEventListener('paste', el.$_keyfilterPasteEvent); + }, + unbindEvents(el) { + el.removeEventListener('keypress', el.$_keyfilterKeydownEvent); + el.removeEventListener('paste', el.$_keyfilterPasteEvent); + + el.$_keyfilterKeydownEvent = null; + el.$_keyfilterPasteEvent = null; + }, + onKeydown(event, target) { + if (event.ctrlKey || event.altKey || event.metaKey || event.key === 'Tab') { + return; + } + + let regex = this.getRegex(target); + + if (regex === '') { + return; + } + + let testKey = `${event.key}`; + + if (target.$_pkeyfilterValidateOnly) { + testKey = `${event.target.value}${event.key}`; + } + + if (!regex.test(testKey)) { + // runs before @update:modelValue emit + event.preventDefault(); + } + }, + onPaste(event, target) { + let regex = this.getRegex(target); + + if (regex === '') { + return; + } + + const clipboard = event.clipboardData.getData('text'); + let testKey = ''; + + // loop over each letter pasted and if any fail prevent the paste + [...clipboard].forEach((c) => { + if (target.$_pkeyfilterValidateOnly) { + testKey += c; + } else { + testKey = c; + } + + if (!regex.test(testKey)) { + event.preventDefault(); + + return false; + } + }); + } + } +}); + +export default KeyFilter; diff --git a/packages/primevue/src/keyfilter/package.json b/packages/primevue/src/keyfilter/package.json new file mode 100644 index 000000000..88fe424e3 --- /dev/null +++ b/packages/primevue/src/keyfilter/package.json @@ -0,0 +1,5 @@ +{ + "main": "./KeyFilter.js", + "module": "./KeyFilter.js", + "types": "./KeyFilter.d.ts" +} diff --git a/packages/primevue/src/keyfilter/style/KeyFilterStyle.d.ts b/packages/primevue/src/keyfilter/style/KeyFilterStyle.d.ts new file mode 100644 index 000000000..2aa7ca884 --- /dev/null +++ b/packages/primevue/src/keyfilter/style/KeyFilterStyle.d.ts @@ -0,0 +1,14 @@ +/** + * + * KeyFilter is a built-in feature of InputText to restrict user input based on a regular expression. + * + * [Live Demo](https://primevue.org/keyfilter) + * + * @module keyfilterstyle + * + */ +import type { BaseStyle } from '@primevue/core/base/style'; + +export enum KeyFilterClasses {} + +export interface KeyFilterStyle extends BaseStyle {} diff --git a/packages/primevue/src/keyfilter/style/KeyFilterStyle.js b/packages/primevue/src/keyfilter/style/KeyFilterStyle.js new file mode 100644 index 000000000..b50d3b3fb --- /dev/null +++ b/packages/primevue/src/keyfilter/style/KeyFilterStyle.js @@ -0,0 +1,5 @@ +import BaseStyle from '@primevue/core/base/style'; + +export default BaseStyle.extend({ + name: 'keyfilter-directive' +}); diff --git a/packages/primevue/src/keyfilter/style/package.json b/packages/primevue/src/keyfilter/style/package.json new file mode 100644 index 000000000..bef6b977f --- /dev/null +++ b/packages/primevue/src/keyfilter/style/package.json @@ -0,0 +1,6 @@ +{ + "main": "./KeyFilterStyle.js", + "module": "./KeyFilterStyle.js", + "types": "./KeyFilterStyle.d.ts", + "sideEffects": false +}