Refactor #5612 - Chips / InputChips
parent
55ae9908d1
commit
365879a41c
|
@ -2,77 +2,30 @@
|
||||||
*
|
*
|
||||||
* Chips groups a collection of contents in tabs.
|
* Chips groups a collection of contents in tabs.
|
||||||
*
|
*
|
||||||
* [Live Demo](https://www.primevue.org/chips/)
|
* [Live Demo](https://www.primevue.org/inputchips/)
|
||||||
*
|
*
|
||||||
* @module chips
|
* @module chips
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { InputHTMLAttributes, VNode } from 'vue';
|
import 'vue';
|
||||||
import { ComponentHooks } from '../basecomponent';
|
import * as InputChips from '../inputchips';
|
||||||
import { ChipPassThroughOptions } from '../chip';
|
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
|
||||||
import { PassThroughOptions } from '../passthrough';
|
|
||||||
import { ClassComponent, DesignToken, GlobalComponentConstructor, PassThrough } from '../ts-helpers';
|
|
||||||
|
|
||||||
export declare type ChipsPassThroughOptionType = ChipsPassThroughAttributes | ((options: ChipsPassThroughMethodOptions) => ChipsPassThroughAttributes | string) | string | null | undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom passthrough(pt) option method.
|
* Custom passthrough(pt) option method.
|
||||||
*/
|
*/
|
||||||
export interface ChipsPassThroughMethodOptions {
|
export interface ChipsPassThroughMethodOptions extends InputChips.InputChipsPassThroughMethodOptions {}
|
||||||
/**
|
|
||||||
* Defines instance.
|
|
||||||
*/
|
|
||||||
instance: any;
|
|
||||||
/**
|
|
||||||
* Defines valid properties.
|
|
||||||
*/
|
|
||||||
props: ChipsProps;
|
|
||||||
/**
|
|
||||||
* Defines current inline state.
|
|
||||||
*/
|
|
||||||
state: ChipsState;
|
|
||||||
/**
|
|
||||||
* Defines valid attributes.
|
|
||||||
*/
|
|
||||||
attrs: any;
|
|
||||||
/**
|
|
||||||
* Defines parent options.
|
|
||||||
*/
|
|
||||||
parent: any;
|
|
||||||
/**
|
|
||||||
* Defines passthrough(pt) options in global config.
|
|
||||||
*/
|
|
||||||
global: object | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom shared passthrough(pt) option method.
|
* Custom shared passthrough(pt) option method.
|
||||||
*/
|
*/
|
||||||
export interface ChipsSharedPassThroughMethodOptions {
|
export interface ChipsSharedPassThroughMethodOptions extends InputChips.InputChipsSharedPassThroughMethodOptions {}
|
||||||
/**
|
|
||||||
* Defines valid properties.
|
|
||||||
*/
|
|
||||||
props: ChipsProps;
|
|
||||||
/**
|
|
||||||
* Defines current inline state.
|
|
||||||
*/
|
|
||||||
state: ChipsState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom add event.
|
* Custom add event.
|
||||||
* @see {@link ChipsEmits.add}
|
* @see {@link ChipsEmits.add}
|
||||||
*/
|
*/
|
||||||
export interface ChipsAddEvent {
|
export interface ChipsAddEvent extends InputChips.InputChipsAddEvent {}
|
||||||
/**
|
|
||||||
* Browser event.
|
|
||||||
*/
|
|
||||||
originalEvent: Event;
|
|
||||||
/**
|
|
||||||
* Added/Removed item value.
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom remove event.
|
* Custom remove event.
|
||||||
|
@ -85,235 +38,39 @@ export interface ChipsRemoveEvent extends ChipsAddEvent {}
|
||||||
* Custom passthrough(pt) options.
|
* Custom passthrough(pt) options.
|
||||||
* @see {@link ChipsProps.pt}
|
* @see {@link ChipsProps.pt}
|
||||||
*/
|
*/
|
||||||
export interface ChipsPassThroughOptions {
|
export interface ChipsPassThroughOptions extends InputChips.InputChipsPassThroughOptions {}
|
||||||
/**
|
|
||||||
* Used to pass attributes to the root's DOM element.
|
|
||||||
*/
|
|
||||||
root?: ChipsPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the container's DOM element.
|
|
||||||
*/
|
|
||||||
container?: ChipsPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the token's DOM element.
|
|
||||||
*/
|
|
||||||
token?: ChipsPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the Chip component.
|
|
||||||
* @see {@link ChipPassThroughOptions}
|
|
||||||
*/
|
|
||||||
label?: ChipPassThroughOptions<ChipsSharedPassThroughMethodOptions>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the remove token icon's DOM element.
|
|
||||||
*/
|
|
||||||
removeTokenIcon?: ChipsPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the input token's DOM element.
|
|
||||||
*/
|
|
||||||
inputToken?: ChipsPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the input's DOM element.
|
|
||||||
*/
|
|
||||||
input?: ChipsPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to manage all lifecycle hooks.
|
|
||||||
* @see {@link BaseComponent.ComponentHooks}
|
|
||||||
*/
|
|
||||||
hooks?: ComponentHooks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom passthrough attributes for each DOM elements
|
* Custom passthrough attributes for each DOM elements
|
||||||
*/
|
*/
|
||||||
export interface ChipsPassThroughAttributes {
|
export interface ChipsPassThroughAttributes extends InputChips.InputChipsPassThroughAttributes {}
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines current inline state in Chips component.
|
* Defines current inline state in Chips component.
|
||||||
*/
|
*/
|
||||||
export interface ChipsState {
|
export interface ChipsState extends InputChips.InputChipsState {}
|
||||||
/**
|
|
||||||
* Current id state as a string.
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Current input value as a string.
|
|
||||||
*/
|
|
||||||
inputValue: string;
|
|
||||||
/**
|
|
||||||
* Current focused state as a boolean.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
focused: boolean;
|
|
||||||
/**
|
|
||||||
* Current focused item index state as a number.
|
|
||||||
*/
|
|
||||||
focusedIndex: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines valid properties in Chips component.
|
* Defines valid properties in Chips component.
|
||||||
*/
|
*/
|
||||||
export interface ChipsProps {
|
export interface ChipsProps extends InputChips.InputChipsProps {}
|
||||||
/**
|
|
||||||
* Value of the component.
|
|
||||||
*/
|
|
||||||
modelValue?: any[];
|
|
||||||
/**
|
|
||||||
* Maximum number of entries allowed.
|
|
||||||
*/
|
|
||||||
max?: number | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to add an item when the input loses focus.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
addOnBlur?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to allow duplicate values or not.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
allowDuplicate?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Separator char to add an item when pressed in addition to the enter key.
|
|
||||||
*/
|
|
||||||
separator?: string | any;
|
|
||||||
/**
|
|
||||||
* Identifier of the focus input to match a label defined for the chips.
|
|
||||||
*/
|
|
||||||
inputId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the input field.
|
|
||||||
*/
|
|
||||||
inputClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the input field.
|
|
||||||
*/
|
|
||||||
inputStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Used to pass all properties of the HTMLInputElement to the focusable input element inside the component.
|
|
||||||
* @deprecated since v3.26.0. Use 'pt' property instead.
|
|
||||||
*/
|
|
||||||
inputProps?: InputHTMLAttributes | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in chip remove action.
|
|
||||||
* @deprecated since v3.27.0. Use 'removetokenicon' slot.
|
|
||||||
*/
|
|
||||||
removeTokenIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that the component should have invalid state style.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
invalid?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When present, it specifies that the element should be disabled.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
disabled?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies the input variant of the component.
|
|
||||||
* @defaultValue outlined
|
|
||||||
*/
|
|
||||||
variant?: 'outlined' | 'filled' | undefined;
|
|
||||||
/**
|
|
||||||
* Placeholder text for the input.
|
|
||||||
*/
|
|
||||||
placeholder?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Establishes relationships between the component and label(s) where its value should be one or more element IDs.
|
|
||||||
*/
|
|
||||||
ariaLabelledby?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Establishes a string value that labels the component.
|
|
||||||
*/
|
|
||||||
ariaLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* It generates scoped CSS variables using design tokens for the component.
|
|
||||||
*/
|
|
||||||
dt?: DesignToken<any>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to DOM elements inside the component.
|
|
||||||
* @type {ChipsPassThroughOptions}
|
|
||||||
*/
|
|
||||||
pt?: PassThrough<ChipsPassThroughOptions>;
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Defines valid slots in Chips slots.
|
* Defines valid slots in Chips slots.
|
||||||
*/
|
*/
|
||||||
export interface ChipsSlots {
|
export interface ChipsSlots extends InputChips.InputChipsSlots {}
|
||||||
/**
|
|
||||||
* Custom chip template.
|
|
||||||
* @param {Object} scope - chip slot's params.
|
|
||||||
*/
|
|
||||||
chip(scope: {
|
|
||||||
/**
|
|
||||||
* Value of the component
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom remove token icon template.
|
|
||||||
* @param {Object} scope - remove token icon slot's params.
|
|
||||||
*/
|
|
||||||
removetokenicon(scope: {
|
|
||||||
/**
|
|
||||||
* Style class of the icon.
|
|
||||||
*/
|
|
||||||
class: string;
|
|
||||||
/**
|
|
||||||
* Index of the token.
|
|
||||||
*/
|
|
||||||
index: number;
|
|
||||||
/**
|
|
||||||
* Remove token icon function.
|
|
||||||
* @param {Event} event - Browser event
|
|
||||||
* @deprecated since v3.39.0. Use 'removeCallback' property instead.
|
|
||||||
*/
|
|
||||||
onClick: (event: Event, index: number) => void;
|
|
||||||
/**
|
|
||||||
* Remove token icon function.
|
|
||||||
* @param {Event} event - Browser event
|
|
||||||
*/
|
|
||||||
removeCallback: (event: Event, index: number) => void;
|
|
||||||
}): VNode[];
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Defines valid emits in Chips component.
|
* Defines valid emits in Chips component.
|
||||||
*/
|
*/
|
||||||
export interface ChipsEmits {
|
export interface ChipsEmits extends InputChips.InputChipsEmits {}
|
||||||
/**
|
|
||||||
* Emitted when the value changes.
|
|
||||||
* @param {*} value - New value.
|
|
||||||
*/
|
|
||||||
'update:modelValue'(value: any[]): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke when a chip is added.
|
|
||||||
* @param {ChipsAddEvent} event - Custom add event.
|
|
||||||
*/
|
|
||||||
add(event: ChipsAddEvent): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke when a chip is removed.
|
|
||||||
* @param {ChipsRemoveEvent} event - Custom remove event.
|
|
||||||
*/
|
|
||||||
remove(event: ChipsRemoveEvent): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated Deprecated since v4. Use InputChips component instead.
|
||||||
|
*
|
||||||
* **PrimeVue - Chips**
|
* **PrimeVue - Chips**
|
||||||
*
|
*
|
||||||
* _Chips is used to enter multiple values on an input field._
|
* _Chips is used to enter multiple values on an input field._
|
||||||
*
|
*
|
||||||
* [Live Demo](https://www.primevue.org/chips/)
|
* [Live Demo](https://www.primevue.org/inputchips/)
|
||||||
* --- ---
|
* --- ---
|
||||||
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
|
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,269 +1,11 @@
|
||||||
<template>
|
|
||||||
<div :class="cx('root')" v-bind="ptmi('root')">
|
|
||||||
<ul
|
|
||||||
ref="container"
|
|
||||||
:class="cx('container')"
|
|
||||||
tabindex="-1"
|
|
||||||
role="listbox"
|
|
||||||
aria-orientation="horizontal"
|
|
||||||
:aria-labelledby="ariaLabelledby"
|
|
||||||
:aria-label="ariaLabel"
|
|
||||||
:aria-activedescendant="focused ? focusedOptionId : undefined"
|
|
||||||
@click="onWrapperClick()"
|
|
||||||
@focus="onContainerFocus"
|
|
||||||
@blur="onContainerBlur"
|
|
||||||
@keydown="onContainerKeyDown"
|
|
||||||
v-bind="ptm('container')"
|
|
||||||
>
|
|
||||||
<li
|
|
||||||
v-for="(val, i) of modelValue"
|
|
||||||
:key="`${i}_${val}`"
|
|
||||||
:id="id + '_chips_item_' + i"
|
|
||||||
role="option"
|
|
||||||
:class="cx('token', { index: i })"
|
|
||||||
:aria-label="val"
|
|
||||||
:aria-selected="true"
|
|
||||||
:aria-setsize="modelValue.length"
|
|
||||||
:aria-posinset="i + 1"
|
|
||||||
v-bind="ptm('token')"
|
|
||||||
:data-p-focused="focusedIndex === i"
|
|
||||||
>
|
|
||||||
<slot name="chip" :class="cx('label')" :index="i" :value="val" :removeCallback="(event) => removeOption(event, i)">
|
|
||||||
<Chip :class="cx('label')" :label="val" :removeIcon="removeTokenIcon" removable :unstyled="unstyled" @remove="removeItem($event, i)" :pt="ptm('label')">
|
|
||||||
<template #removeicon>
|
|
||||||
<slot name="removetokenicon" :class="cx('removeTokenIcon')" :index="i" :removeCallback="(event) => removeItem(event, i)" />
|
|
||||||
</template>
|
|
||||||
</Chip>
|
|
||||||
</slot>
|
|
||||||
</li>
|
|
||||||
<li :class="cx('inputToken')" role="option" v-bind="ptm('inputToken')">
|
|
||||||
<input
|
|
||||||
ref="input"
|
|
||||||
:id="inputId"
|
|
||||||
type="text"
|
|
||||||
:class="inputClass"
|
|
||||||
:style="inputStyle"
|
|
||||||
:disabled="disabled || maxedOut"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:aria-invalid="invalid || undefined"
|
|
||||||
@focus="onFocus($event)"
|
|
||||||
@blur="onBlur($event)"
|
|
||||||
@input="onInput"
|
|
||||||
@keydown="onKeyDown($event)"
|
|
||||||
@paste="onPaste($event)"
|
|
||||||
v-bind="{ ...inputProps, ...ptm('input') }"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Chip from 'primevue/chip';
|
import InputChips from 'primevue/inputchips';
|
||||||
import { UniqueComponentId } from 'primevue/utils';
|
|
||||||
import BaseChips from './BaseChips.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Chips',
|
name: 'Chips',
|
||||||
extends: BaseChips,
|
extends: InputChips,
|
||||||
inheritAttrs: false,
|
|
||||||
emits: ['update:modelValue', 'add', 'remove', 'focus', 'blur'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
id: this.$attrs.id,
|
|
||||||
inputValue: null,
|
|
||||||
focused: false,
|
|
||||||
focusedIndex: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$attrs.id': function (newValue) {
|
|
||||||
this.id = newValue || UniqueComponentId();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.id = this.id || UniqueComponentId();
|
console.warn('Deprecated since v4. Use InputChips component instead.');
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onWrapperClick() {
|
|
||||||
this.$refs.input.focus();
|
|
||||||
},
|
|
||||||
onInput(event) {
|
|
||||||
this.inputValue = event.target.value;
|
|
||||||
this.focusedIndex = null;
|
|
||||||
},
|
|
||||||
onFocus(event) {
|
|
||||||
this.focused = true;
|
|
||||||
this.focusedIndex = null;
|
|
||||||
this.$emit('focus', event);
|
|
||||||
},
|
|
||||||
onBlur(event) {
|
|
||||||
this.focused = false;
|
|
||||||
this.focusedIndex = null;
|
|
||||||
|
|
||||||
if (this.addOnBlur) {
|
|
||||||
this.addItem(event, event.target.value, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('blur', event);
|
|
||||||
},
|
|
||||||
onKeyDown(event) {
|
|
||||||
const inputValue = event.target.value;
|
|
||||||
|
|
||||||
switch (event.code) {
|
|
||||||
case 'Backspace':
|
|
||||||
if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
|
||||||
if (this.focusedIndex !== null) {
|
|
||||||
this.removeItem(event, this.focusedIndex);
|
|
||||||
} else this.removeItem(event, this.modelValue.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Enter':
|
|
||||||
case 'NumpadEnter':
|
|
||||||
if (inputValue && inputValue.trim().length && !this.maxedOut) {
|
|
||||||
this.addItem(event, inputValue, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowLeft':
|
|
||||||
if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
|
||||||
this.$refs.container.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowRight':
|
|
||||||
event.stopPropagation();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (this.separator) {
|
|
||||||
if (this.separator === event.key || event.key.match(this.separator)) {
|
|
||||||
this.addItem(event, inputValue, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPaste(event) {
|
|
||||||
if (this.separator) {
|
|
||||||
let separator = this.separator.replace('\\n', '\n').replace('\\r', '\r').replace('\\t', '\t');
|
|
||||||
let pastedData = (event.clipboardData || window['clipboardData']).getData('Text');
|
|
||||||
|
|
||||||
if (pastedData) {
|
|
||||||
let value = this.modelValue || [];
|
|
||||||
let pastedValues = pastedData.split(separator);
|
|
||||||
|
|
||||||
pastedValues = pastedValues.filter((val) => this.allowDuplicate || value.indexOf(val) === -1);
|
|
||||||
value = [...value, ...pastedValues];
|
|
||||||
this.updateModel(event, value, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onContainerFocus() {
|
|
||||||
this.focused = true;
|
|
||||||
},
|
|
||||||
onContainerBlur() {
|
|
||||||
this.focusedIndex = -1;
|
|
||||||
this.focused = false;
|
|
||||||
},
|
|
||||||
onContainerKeyDown(event) {
|
|
||||||
switch (event.code) {
|
|
||||||
case 'ArrowLeft':
|
|
||||||
this.onArrowLeftKeyOn(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowRight':
|
|
||||||
this.onArrowRightKeyOn(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Backspace':
|
|
||||||
this.onBackspaceKeyOn(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onArrowLeftKeyOn() {
|
|
||||||
if (this.inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
|
||||||
this.focusedIndex = this.focusedIndex === null ? this.modelValue.length - 1 : this.focusedIndex - 1;
|
|
||||||
if (this.focusedIndex < 0) this.focusedIndex = 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onArrowRightKeyOn() {
|
|
||||||
if (this.inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
|
||||||
if (this.focusedIndex === this.modelValue.length - 1) {
|
|
||||||
this.focusedIndex = null;
|
|
||||||
this.$refs.input.focus();
|
|
||||||
} else {
|
|
||||||
this.focusedIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onBackspaceKeyOn(event) {
|
|
||||||
if (this.focusedIndex !== null) {
|
|
||||||
this.removeItem(event, this.focusedIndex);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateModel(event, value, preventDefault) {
|
|
||||||
this.$emit('update:modelValue', value);
|
|
||||||
this.$emit('add', {
|
|
||||||
originalEvent: event,
|
|
||||||
value: value
|
|
||||||
});
|
|
||||||
this.$refs.input.value = '';
|
|
||||||
this.inputValue = '';
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.maxedOut && (this.focused = false);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
if (preventDefault) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addItem(event, item, preventDefault) {
|
|
||||||
if (item && item.trim().length) {
|
|
||||||
let value = this.modelValue ? [...this.modelValue] : [];
|
|
||||||
|
|
||||||
if (this.allowDuplicate || value.indexOf(item) === -1) {
|
|
||||||
value.push(item);
|
|
||||||
this.updateModel(event, value, preventDefault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeItem(event, index) {
|
|
||||||
if (this.disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let values = [...this.modelValue];
|
|
||||||
const removedItem = values.splice(index, 1);
|
|
||||||
|
|
||||||
this.focusedIndex = null;
|
|
||||||
this.$refs.input.focus();
|
|
||||||
this.$emit('update:modelValue', values);
|
|
||||||
this.$emit('remove', {
|
|
||||||
originalEvent: event,
|
|
||||||
value: removedItem
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
maxedOut() {
|
|
||||||
return this.max && this.modelValue && this.max === this.modelValue.length;
|
|
||||||
},
|
|
||||||
focusedOptionId() {
|
|
||||||
return this.focusedIndex !== null ? `${this.id}_chips_item_${this.focusedIndex}` : null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Chip
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { BaseStyle } from '../../base/style';
|
import { InputChipsStyle } from '../../inputchips/style/InputChipsStyle';
|
||||||
|
|
||||||
export interface ChipsStyle extends BaseStyle {}
|
export interface ChipsStyle extends InputChipsStyle {}
|
||||||
|
|
|
@ -1,29 +1,5 @@
|
||||||
import BaseStyle from 'primevue/base/style';
|
import BaseStyle from 'primevue/base/style';
|
||||||
|
|
||||||
const classes = {
|
|
||||||
root: ({ instance, props }) => [
|
|
||||||
'p-inputchips p-component p-inputwrapper',
|
|
||||||
{
|
|
||||||
'p-disabled': props.disabled,
|
|
||||||
'p-invalid': props.invalid,
|
|
||||||
'p-focus': instance.focused,
|
|
||||||
'p-inputwrapper-filled': (props.modelValue && props.modelValue.length) || (instance.inputValue && instance.inputValue.length),
|
|
||||||
'p-inputwrapper-focus': instance.focused
|
|
||||||
}
|
|
||||||
],
|
|
||||||
container: ({ props, instance }) => [
|
|
||||||
'p-inputchips-input',
|
|
||||||
{
|
|
||||||
'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
token: ({ state, index }) => ['p-inputchips-chip', { 'p-focus': state.focusedIndex === index }],
|
|
||||||
label: 'p-inputchips-chip-label',
|
|
||||||
removeTokenIcon: 'p-inputchips-chip-icon',
|
|
||||||
inputToken: 'p-inputchips-input-item'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaseStyle.extend({
|
export default BaseStyle.extend({
|
||||||
name: 'chips',
|
name: 'chips'
|
||||||
classes
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import BaseComponent from 'primevue/basecomponent';
|
import BaseComponent from 'primevue/basecomponent';
|
||||||
import ChipsStyle from 'primevue/chips/style';
|
import InputChipsStyle from 'primevue/inputchips/style';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BaseChips',
|
name: 'BaseInputChips',
|
||||||
extends: BaseComponent,
|
extends: BaseComponent,
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
@ -71,7 +71,7 @@ export default {
|
||||||
default: null
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: ChipsStyle,
|
style: InputChipsStyle,
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
$parentInstance: this
|
$parentInstance: this
|
|
@ -0,0 +1,331 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* InputChips groups a collection of contents in tabs.
|
||||||
|
*
|
||||||
|
* [Live Demo](https://www.primevue.org/inputchips/)
|
||||||
|
*
|
||||||
|
* @module inputchips
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { InputHTMLAttributes, VNode } from 'vue';
|
||||||
|
import { ComponentHooks } from '../basecomponent';
|
||||||
|
import { ChipPassThroughOptions } from '../chip';
|
||||||
|
import { PassThroughOptions } from '../passthrough';
|
||||||
|
import { ClassComponent, DesignToken, GlobalComponentConstructor, PassThrough } from '../ts-helpers';
|
||||||
|
|
||||||
|
export declare type InputChipsPassThroughOptionType = InputChipsPassThroughAttributes | ((options: InputChipsPassThroughMethodOptions) => InputChipsPassThroughAttributes | string) | string | null | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom passthrough(pt) option method.
|
||||||
|
*/
|
||||||
|
export interface InputChipsPassThroughMethodOptions {
|
||||||
|
/**
|
||||||
|
* Defines instance.
|
||||||
|
*/
|
||||||
|
instance: any;
|
||||||
|
/**
|
||||||
|
* Defines valid properties.
|
||||||
|
*/
|
||||||
|
props: InputChipsProps;
|
||||||
|
/**
|
||||||
|
* Defines current inline state.
|
||||||
|
*/
|
||||||
|
state: InputChipsState;
|
||||||
|
/**
|
||||||
|
* Defines valid attributes.
|
||||||
|
*/
|
||||||
|
attrs: any;
|
||||||
|
/**
|
||||||
|
* Defines parent options.
|
||||||
|
*/
|
||||||
|
parent: any;
|
||||||
|
/**
|
||||||
|
* Defines passthrough(pt) options in global config.
|
||||||
|
*/
|
||||||
|
global: object | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom shared passthrough(pt) option method.
|
||||||
|
*/
|
||||||
|
export interface InputChipsSharedPassThroughMethodOptions {
|
||||||
|
/**
|
||||||
|
* Defines valid properties.
|
||||||
|
*/
|
||||||
|
props: InputChipsProps;
|
||||||
|
/**
|
||||||
|
* Defines current inline state.
|
||||||
|
*/
|
||||||
|
state: InputChipsState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom add event.
|
||||||
|
* @see {@link InputChipsEmits.add}
|
||||||
|
*/
|
||||||
|
export interface InputChipsAddEvent {
|
||||||
|
/**
|
||||||
|
* Browser event.
|
||||||
|
*/
|
||||||
|
originalEvent: Event;
|
||||||
|
/**
|
||||||
|
* Added/Removed item value.
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom remove event.
|
||||||
|
* @see {@link InputChipsEmits.remove}
|
||||||
|
* @extends InputChipsAddEvent
|
||||||
|
*/
|
||||||
|
export interface InputChipsRemoveEvent extends InputChipsAddEvent {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom passthrough(pt) options.
|
||||||
|
* @see {@link InputChipsProps.pt}
|
||||||
|
*/
|
||||||
|
export interface InputChipsPassThroughOptions {
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the root's DOM element.
|
||||||
|
*/
|
||||||
|
root?: InputChipsPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the container's DOM element.
|
||||||
|
*/
|
||||||
|
container?: InputChipsPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the token's DOM element.
|
||||||
|
*/
|
||||||
|
token?: InputChipsPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the Chip component.
|
||||||
|
* @see {@link ChipPassThroughOptions}
|
||||||
|
*/
|
||||||
|
label?: ChipPassThroughOptions<InputChipsSharedPassThroughMethodOptions>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the remove token icon's DOM element.
|
||||||
|
*/
|
||||||
|
removeTokenIcon?: InputChipsPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the input token's DOM element.
|
||||||
|
*/
|
||||||
|
inputToken?: InputChipsPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the input's DOM element.
|
||||||
|
*/
|
||||||
|
input?: InputChipsPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to manage all lifecycle hooks.
|
||||||
|
* @see {@link BaseComponent.ComponentHooks}
|
||||||
|
*/
|
||||||
|
hooks?: ComponentHooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom passthrough attributes for each DOM elements
|
||||||
|
*/
|
||||||
|
export interface InputChipsPassThroughAttributes {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines current inline state in InputChips component.
|
||||||
|
*/
|
||||||
|
export interface InputChipsState {
|
||||||
|
/**
|
||||||
|
* Current id state as a string.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* Current input value as a string.
|
||||||
|
*/
|
||||||
|
inputValue: string;
|
||||||
|
/**
|
||||||
|
* Current focused state as a boolean.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
focused: boolean;
|
||||||
|
/**
|
||||||
|
* Current focused item index state as a number.
|
||||||
|
*/
|
||||||
|
focusedIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines valid properties in InputChips component.
|
||||||
|
*/
|
||||||
|
export interface InputChipsProps {
|
||||||
|
/**
|
||||||
|
* Value of the component.
|
||||||
|
*/
|
||||||
|
modelValue?: any[];
|
||||||
|
/**
|
||||||
|
* Maximum number of entries allowed.
|
||||||
|
*/
|
||||||
|
max?: number | undefined;
|
||||||
|
/**
|
||||||
|
* Whether to add an item when the input loses focus.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
addOnBlur?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Whether to allow duplicate values or not.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
allowDuplicate?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Separator char to add an item when pressed in addition to the enter key.
|
||||||
|
*/
|
||||||
|
separator?: string | any;
|
||||||
|
/**
|
||||||
|
* Identifier of the focus input to match a label defined for the inputchips.
|
||||||
|
*/
|
||||||
|
inputId?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Style class of the input field.
|
||||||
|
*/
|
||||||
|
inputClass?: string | object | undefined;
|
||||||
|
/**
|
||||||
|
* Inline style of the input field.
|
||||||
|
*/
|
||||||
|
inputStyle?: object | undefined;
|
||||||
|
/**
|
||||||
|
* Used to pass all properties of the HTMLInputElement to the focusable input element inside the component.
|
||||||
|
* @deprecated since v3.26.0. Use 'pt' property instead.
|
||||||
|
*/
|
||||||
|
inputProps?: InputHTMLAttributes | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in chip remove action.
|
||||||
|
* @deprecated since v3.27.0. Use 'removetokenicon' slot.
|
||||||
|
*/
|
||||||
|
removeTokenIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* When present, it specifies that the component should have invalid state style.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
invalid?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* When present, it specifies that the element should be disabled.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Specifies the input variant of the component.
|
||||||
|
* @defaultValue outlined
|
||||||
|
*/
|
||||||
|
variant?: 'outlined' | 'filled' | undefined;
|
||||||
|
/**
|
||||||
|
* Placeholder text for the input.
|
||||||
|
*/
|
||||||
|
placeholder?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Establishes relationships between the component and label(s) where its value should be one or more element IDs.
|
||||||
|
*/
|
||||||
|
ariaLabelledby?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Establishes a string value that labels the component.
|
||||||
|
*/
|
||||||
|
ariaLabel?: string | undefined;
|
||||||
|
/**
|
||||||
|
* It generates scoped CSS variables using design tokens for the component.
|
||||||
|
*/
|
||||||
|
dt?: DesignToken<any>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to DOM elements inside the component.
|
||||||
|
* @type {InputChipsPassThroughOptions}
|
||||||
|
*/
|
||||||
|
pt?: PassThrough<InputChipsPassThroughOptions>;
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Defines valid slots in InputChips slots.
|
||||||
|
*/
|
||||||
|
export interface InputChipsSlots {
|
||||||
|
/**
|
||||||
|
* Custom chip template.
|
||||||
|
* @param {Object} scope - chip slot's params.
|
||||||
|
*/
|
||||||
|
chip(scope: {
|
||||||
|
/**
|
||||||
|
* Value of the component
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom remove token icon template.
|
||||||
|
* @param {Object} scope - remove token icon slot's params.
|
||||||
|
*/
|
||||||
|
removetokenicon(scope: {
|
||||||
|
/**
|
||||||
|
* Style class of the icon.
|
||||||
|
*/
|
||||||
|
class: string;
|
||||||
|
/**
|
||||||
|
* Index of the token.
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
/**
|
||||||
|
* Remove token icon function.
|
||||||
|
* @param {Event} event - Browser event
|
||||||
|
* @deprecated since v3.39.0. Use 'removeCallback' property instead.
|
||||||
|
*/
|
||||||
|
onClick: (event: Event, index: number) => void;
|
||||||
|
/**
|
||||||
|
* Remove token icon function.
|
||||||
|
* @param {Event} event - Browser event
|
||||||
|
*/
|
||||||
|
removeCallback: (event: Event, index: number) => void;
|
||||||
|
}): VNode[];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Defines valid emits in InputChips component.
|
||||||
|
*/
|
||||||
|
export interface InputChipsEmits {
|
||||||
|
/**
|
||||||
|
* Emitted when the value changes.
|
||||||
|
* @param {*} value - New value.
|
||||||
|
*/
|
||||||
|
'update:modelValue'(value: any[]): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke when a chip is added.
|
||||||
|
* @param {InputChipsAddEvent} event - Custom add event.
|
||||||
|
*/
|
||||||
|
add(event: InputChipsAddEvent): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke when a chip is removed.
|
||||||
|
* @param {InputChipsRemoveEvent} event - Custom remove event.
|
||||||
|
*/
|
||||||
|
remove(event: InputChipsRemoveEvent): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **PrimeVue - InputChips**
|
||||||
|
*
|
||||||
|
* _InputChips is used to enter multiple values on an input field._
|
||||||
|
*
|
||||||
|
* [Live Demo](https://www.primevue.org/inputchips/)
|
||||||
|
* --- ---
|
||||||
|
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
|
||||||
|
*
|
||||||
|
* @group Component
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
declare class InputChips extends ClassComponent<InputChipsProps, InputChipsSlots, InputChipsEmits> {}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
InputChips: GlobalComponentConstructor<InputChips>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputChips;
|
|
@ -1,11 +1,11 @@
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import Chips from './Chips.vue';
|
import InputChips from './InputChips.vue';
|
||||||
|
|
||||||
describe('Chips.vue', () => {
|
describe('InputChips.vue', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = mount(Chips, {
|
wrapper = mount(InputChips, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: null
|
modelValue: null
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ describe('Chips.vue', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should exist', () => {
|
it('should exist', () => {
|
||||||
expect(wrapper.find('.p-chips.p-component.p-inputwrapper').exists()).toBe(true);
|
expect(wrapper.find('.p-inputchips.p-component.p-inputwrapper').exists()).toBe(true);
|
||||||
expect(wrapper.find('ul.p-chips-multiple-container').exists()).toBe(true);
|
expect(wrapper.find('ul.p-inputchips-multiple-container').exists()).toBe(true);
|
||||||
expect(wrapper.find('li.p-chips-input-token').exists()).toBe(true);
|
expect(wrapper.find('li.p-inputchips-input-token').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add item', async () => {
|
it('should add item', async () => {
|
||||||
|
@ -26,9 +26,9 @@ describe('Chips.vue', () => {
|
||||||
await wrapper.setProps({ modelValue: ['PrimeVue'] });
|
await wrapper.setProps({ modelValue: ['PrimeVue'] });
|
||||||
|
|
||||||
expect(addItem).toHaveBeenCalled();
|
expect(addItem).toHaveBeenCalled();
|
||||||
expect(wrapper.findAll('.p-chips-token').length).toBe(1);
|
expect(wrapper.findAll('.p-inputchips-token').length).toBe(1);
|
||||||
expect(wrapper.find('.p-chips-token-label').exists()).toBe(true);
|
expect(wrapper.find('.p-inputchips-token-label').exists()).toBe(true);
|
||||||
expect(wrapper.find('.p-chips-token-label').text()).toBe('PrimeVue');
|
expect(wrapper.find('.p-inputchips-token-label').text()).toBe('PrimeVue');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have correct custom chip removal icon', async () => {
|
it('should have correct custom chip removal icon', async () => {
|
||||||
|
@ -37,7 +37,7 @@ describe('Chips.vue', () => {
|
||||||
removeTokenIcon: 'pi pi-discord'
|
removeTokenIcon: 'pi pi-discord'
|
||||||
});
|
});
|
||||||
|
|
||||||
const icon = wrapper.find('.p-chips-token-icon');
|
const icon = wrapper.find('.p-inputchips-token-icon');
|
||||||
|
|
||||||
expect(icon.classes()).toContain('pi-discord');
|
expect(icon.classes()).toContain('pi-discord');
|
||||||
});
|
});
|
|
@ -0,0 +1,269 @@
|
||||||
|
<template>
|
||||||
|
<div :class="cx('root')" v-bind="ptmi('root')">
|
||||||
|
<ul
|
||||||
|
ref="container"
|
||||||
|
:class="cx('container')"
|
||||||
|
tabindex="-1"
|
||||||
|
role="listbox"
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
:aria-labelledby="ariaLabelledby"
|
||||||
|
:aria-label="ariaLabel"
|
||||||
|
:aria-activedescendant="focused ? focusedOptionId : undefined"
|
||||||
|
@click="onWrapperClick()"
|
||||||
|
@focus="onContainerFocus"
|
||||||
|
@blur="onContainerBlur"
|
||||||
|
@keydown="onContainerKeyDown"
|
||||||
|
v-bind="ptm('container')"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="(val, i) of modelValue"
|
||||||
|
:key="`${i}_${val}`"
|
||||||
|
:id="id + '_inputchips_item_' + i"
|
||||||
|
role="option"
|
||||||
|
:class="cx('token', { index: i })"
|
||||||
|
:aria-label="val"
|
||||||
|
:aria-selected="true"
|
||||||
|
:aria-setsize="modelValue.length"
|
||||||
|
:aria-posinset="i + 1"
|
||||||
|
v-bind="ptm('token')"
|
||||||
|
:data-p-focused="focusedIndex === i"
|
||||||
|
>
|
||||||
|
<slot name="chip" :class="cx('label')" :index="i" :value="val" :removeCallback="(event) => removeOption(event, i)">
|
||||||
|
<Chip :class="cx('label')" :label="val" :removeIcon="removeTokenIcon" removable :unstyled="unstyled" @remove="removeItem($event, i)" :pt="ptm('label')">
|
||||||
|
<template #removeicon>
|
||||||
|
<slot name="removetokenicon" :class="cx('removeTokenIcon')" :index="i" :removeCallback="(event) => removeItem(event, i)" />
|
||||||
|
</template>
|
||||||
|
</Chip>
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
<li :class="cx('inputToken')" role="option" v-bind="ptm('inputToken')">
|
||||||
|
<input
|
||||||
|
ref="input"
|
||||||
|
:id="inputId"
|
||||||
|
type="text"
|
||||||
|
:class="inputClass"
|
||||||
|
:style="inputStyle"
|
||||||
|
:disabled="disabled || maxedOut"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:aria-invalid="invalid || undefined"
|
||||||
|
@focus="onFocus($event)"
|
||||||
|
@blur="onBlur($event)"
|
||||||
|
@input="onInput"
|
||||||
|
@keydown="onKeyDown($event)"
|
||||||
|
@paste="onPaste($event)"
|
||||||
|
v-bind="{ ...inputProps, ...ptm('input') }"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Chip from 'primevue/chip';
|
||||||
|
import { UniqueComponentId } from 'primevue/utils';
|
||||||
|
import BaseInputChips from './BaseInputChips.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'InputChips',
|
||||||
|
extends: BaseInputChips,
|
||||||
|
inheritAttrs: false,
|
||||||
|
emits: ['update:modelValue', 'add', 'remove', 'focus', 'blur'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: this.$attrs.id,
|
||||||
|
inputValue: null,
|
||||||
|
focused: false,
|
||||||
|
focusedIndex: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$attrs.id': function (newValue) {
|
||||||
|
this.id = newValue || UniqueComponentId();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.id = this.id || UniqueComponentId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onWrapperClick() {
|
||||||
|
this.$refs.input.focus();
|
||||||
|
},
|
||||||
|
onInput(event) {
|
||||||
|
this.inputValue = event.target.value;
|
||||||
|
this.focusedIndex = null;
|
||||||
|
},
|
||||||
|
onFocus(event) {
|
||||||
|
this.focused = true;
|
||||||
|
this.focusedIndex = null;
|
||||||
|
this.$emit('focus', event);
|
||||||
|
},
|
||||||
|
onBlur(event) {
|
||||||
|
this.focused = false;
|
||||||
|
this.focusedIndex = null;
|
||||||
|
|
||||||
|
if (this.addOnBlur) {
|
||||||
|
this.addItem(event, event.target.value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('blur', event);
|
||||||
|
},
|
||||||
|
onKeyDown(event) {
|
||||||
|
const inputValue = event.target.value;
|
||||||
|
|
||||||
|
switch (event.code) {
|
||||||
|
case 'Backspace':
|
||||||
|
if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
||||||
|
if (this.focusedIndex !== null) {
|
||||||
|
this.removeItem(event, this.focusedIndex);
|
||||||
|
} else this.removeItem(event, this.modelValue.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Enter':
|
||||||
|
case 'NumpadEnter':
|
||||||
|
if (inputValue && inputValue.trim().length && !this.maxedOut) {
|
||||||
|
this.addItem(event, inputValue, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowLeft':
|
||||||
|
if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
||||||
|
this.$refs.container.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowRight':
|
||||||
|
event.stopPropagation();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (this.separator) {
|
||||||
|
if (this.separator === event.key || event.key.match(this.separator)) {
|
||||||
|
this.addItem(event, inputValue, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPaste(event) {
|
||||||
|
if (this.separator) {
|
||||||
|
let separator = this.separator.replace('\\n', '\n').replace('\\r', '\r').replace('\\t', '\t');
|
||||||
|
let pastedData = (event.clipboardData || window['clipboardData']).getData('Text');
|
||||||
|
|
||||||
|
if (pastedData) {
|
||||||
|
let value = this.modelValue || [];
|
||||||
|
let pastedValues = pastedData.split(separator);
|
||||||
|
|
||||||
|
pastedValues = pastedValues.filter((val) => this.allowDuplicate || value.indexOf(val) === -1);
|
||||||
|
value = [...value, ...pastedValues];
|
||||||
|
this.updateModel(event, value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onContainerFocus() {
|
||||||
|
this.focused = true;
|
||||||
|
},
|
||||||
|
onContainerBlur() {
|
||||||
|
this.focusedIndex = -1;
|
||||||
|
this.focused = false;
|
||||||
|
},
|
||||||
|
onContainerKeyDown(event) {
|
||||||
|
switch (event.code) {
|
||||||
|
case 'ArrowLeft':
|
||||||
|
this.onArrowLeftKeyOn(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowRight':
|
||||||
|
this.onArrowRightKeyOn(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Backspace':
|
||||||
|
this.onBackspaceKeyOn(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onArrowLeftKeyOn() {
|
||||||
|
if (this.inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
||||||
|
this.focusedIndex = this.focusedIndex === null ? this.modelValue.length - 1 : this.focusedIndex - 1;
|
||||||
|
if (this.focusedIndex < 0) this.focusedIndex = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onArrowRightKeyOn() {
|
||||||
|
if (this.inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
|
||||||
|
if (this.focusedIndex === this.modelValue.length - 1) {
|
||||||
|
this.focusedIndex = null;
|
||||||
|
this.$refs.input.focus();
|
||||||
|
} else {
|
||||||
|
this.focusedIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBackspaceKeyOn(event) {
|
||||||
|
if (this.focusedIndex !== null) {
|
||||||
|
this.removeItem(event, this.focusedIndex);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateModel(event, value, preventDefault) {
|
||||||
|
this.$emit('update:modelValue', value);
|
||||||
|
this.$emit('add', {
|
||||||
|
originalEvent: event,
|
||||||
|
value: value
|
||||||
|
});
|
||||||
|
this.$refs.input.value = '';
|
||||||
|
this.inputValue = '';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.maxedOut && (this.focused = false);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
if (preventDefault) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addItem(event, item, preventDefault) {
|
||||||
|
if (item && item.trim().length) {
|
||||||
|
let value = this.modelValue ? [...this.modelValue] : [];
|
||||||
|
|
||||||
|
if (this.allowDuplicate || value.indexOf(item) === -1) {
|
||||||
|
value.push(item);
|
||||||
|
this.updateModel(event, value, preventDefault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItem(event, index) {
|
||||||
|
if (this.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let values = [...this.modelValue];
|
||||||
|
const removedItem = values.splice(index, 1);
|
||||||
|
|
||||||
|
this.focusedIndex = null;
|
||||||
|
this.$refs.input.focus();
|
||||||
|
this.$emit('update:modelValue', values);
|
||||||
|
this.$emit('remove', {
|
||||||
|
originalEvent: event,
|
||||||
|
value: removedItem
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
maxedOut() {
|
||||||
|
return this.max && this.modelValue && this.max === this.modelValue.length;
|
||||||
|
},
|
||||||
|
focusedOptionId() {
|
||||||
|
return this.focusedIndex !== null ? `${this.id}_inputchips_item_${this.focusedIndex}` : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Chip
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"main": "./inputchips.cjs.js",
|
||||||
|
"module": "./inputchips.esm.js",
|
||||||
|
"unpkg": "./inputchips.min.js",
|
||||||
|
"types": "./InputChips.d.ts",
|
||||||
|
"browser": {
|
||||||
|
"./sfc": "./InputChips.vue"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { BaseStyle } from '../../base/style';
|
||||||
|
|
||||||
|
export interface InputChipsStyle extends BaseStyle {}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import BaseStyle from 'primevue/base/style';
|
||||||
|
|
||||||
|
const classes = {
|
||||||
|
root: ({ instance, props }) => [
|
||||||
|
'p-inputchips p-component p-inputwrapper',
|
||||||
|
{
|
||||||
|
'p-disabled': props.disabled,
|
||||||
|
'p-invalid': props.invalid,
|
||||||
|
'p-focus': instance.focused,
|
||||||
|
'p-inputwrapper-filled': (props.modelValue && props.modelValue.length) || (instance.inputValue && instance.inputValue.length),
|
||||||
|
'p-inputwrapper-focus': instance.focused
|
||||||
|
}
|
||||||
|
],
|
||||||
|
container: ({ props, instance }) => [
|
||||||
|
'p-inputchips-input',
|
||||||
|
{
|
||||||
|
'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
token: ({ state, index }) => ['p-inputchips-chip', { 'p-focus': state.focusedIndex === index }],
|
||||||
|
label: 'p-inputchips-chip-label',
|
||||||
|
removeTokenIcon: 'p-inputchips-chip-icon',
|
||||||
|
inputToken: 'p-inputchips-input-item'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseStyle.extend({
|
||||||
|
name: 'inputchips',
|
||||||
|
classes
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"main": "./inputchipsstyle.cjs.js",
|
||||||
|
"module": "./inputchipsstyle.esm.js",
|
||||||
|
"unpkg": "./inputchipsstyle.min.js",
|
||||||
|
"types": "./InputChipsStyle.d.ts"
|
||||||
|
}
|
|
@ -15,41 +15,41 @@ export default {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 0.25rem 0.25rem;
|
padding: 0.25rem 0.25rem;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
color: ${dt('chips.color')};
|
color: ${dt('inputchips.color')};
|
||||||
background: ${dt('chips.background')};
|
background: ${dt('inputchips.background')};
|
||||||
border: 1px solid ${dt('chips.border.color')};
|
border: 1px solid ${dt('inputchips.border.color')};
|
||||||
border-radius: ${dt('rounded.base')};
|
border-radius: ${dt('rounded.base')};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: background-color ${dt('transition.duration')}, color ${dt('transition.duration')}, border-color ${dt('transition.duration')}, outline-color ${dt('transition.duration')};
|
transition: background-color ${dt('transition.duration')}, color ${dt('transition.duration')}, border-color ${dt('transition.duration')}, outline-color ${dt('transition.duration')};
|
||||||
outline-color: transparent;
|
outline-color: transparent;
|
||||||
box-shadow: ${dt('chips.box.shadow')};
|
box-shadow: ${dt('inputchips.box.shadow')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips:not(.p-disabled):hover .p-inputchips-input {
|
.p-inputchips:not(.p-disabled):hover .p-inputchips-input {
|
||||||
border-color: ${dt('chips.hover.border.color')};
|
border-color: ${dt('inputchips.hover.border.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips:not(.p-disabled).p-focus .p-inputchips-input {
|
.p-inputchips:not(.p-disabled).p-focus .p-inputchips-input {
|
||||||
border-color: ${dt('chips.focus.border.color')};
|
border-color: ${dt('inputchips.focus.border.color')};
|
||||||
outline: 0 none;
|
outline: 0 none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips.p-invalid .p-inputchips-input {
|
.p-inputinputchips.p-invalid .p-inputchips-input {
|
||||||
border-color: ${dt('chips.invalid.border.color')};
|
border-color: ${dt('inputchips.invalid.border.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-variant-filled.p-inputchips-input {
|
.p-variant-filled.p-inputchips-input {
|
||||||
background: ${dt('chips.filled.background')};
|
background: ${dt('inputchips.filled.background')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips:not(.p-disabled).p-focus .p-variant-filled.p-inputchips-input {
|
.p-inputchips:not(.p-disabled).p-focus .p-variant-filled.p-inputchips-input {
|
||||||
background: ${dt('chips.filled.focus.background')};
|
background: ${dt('inputchips.filled.focus.background')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips.p-disabled .p-inputchips-input {
|
.p-inputinputchips.p-disabled .p-inputchips-input {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: ${dt('chips.disabled.background')};
|
background: ${dt('inputchips.disabled.background')};
|
||||||
color: ${dt('chips.disabled.color')};
|
color: ${dt('inputchips.disabled.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips-input-item {
|
.p-inputchips-input-item {
|
||||||
|
@ -75,7 +75,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-inputchips-input-item input::placeholder {
|
.p-inputchips-input-item input::placeholder {
|
||||||
color: ${dt('chips.placeholder.color')};
|
color: ${dt('inputchips.placeholder.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-fluid .p-inputchips {
|
.p-fluid .p-inputchips {
|
Loading…
Reference in New Issue