Refactor #5612 - Dropdown / Select
parent
def5d060c0
commit
55ae9908d1
|
@ -2,735 +2,76 @@
|
||||||
*
|
*
|
||||||
* Dropdown also known as Select, is used to choose an item from a collection of options.
|
* Dropdown also known as Select, is used to choose an item from a collection of options.
|
||||||
*
|
*
|
||||||
* [Live Demo](https://www.primevue.org/dropdown/)
|
* [Live Demo](https://www.primevue.org/select/)
|
||||||
*
|
*
|
||||||
* @module dropdown
|
* @module dropdown
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { TransitionProps, VNode } from 'vue';
|
import 'vue';
|
||||||
import { ComponentHooks } from '../basecomponent';
|
import * as Select from '../select';
|
||||||
import { PassThroughOptions } from '../passthrough';
|
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
|
||||||
import { ClassComponent, DesignToken, GlobalComponentConstructor, HintedString, PassThrough } from '../ts-helpers';
|
|
||||||
import { VirtualScrollerItemOptions, VirtualScrollerPassThroughOptionType, VirtualScrollerProps } from '../virtualscroller';
|
|
||||||
|
|
||||||
export declare type DropdownPassThroughOptionType<T = any> = DropdownPassThroughAttributes | ((options: DropdownPassThroughMethodOptions<T>) => DropdownPassThroughAttributes | string) | string | null | undefined;
|
|
||||||
|
|
||||||
export declare type DropdownPassThroughTransitionType<T = any> = TransitionProps | ((options: DropdownPassThroughMethodOptions<T>) => TransitionProps) | undefined;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom passthrough(pt) option method.
|
* Custom passthrough(pt) option method.
|
||||||
*/
|
*/
|
||||||
export interface DropdownPassThroughMethodOptions<T> {
|
export interface DropdownPassThroughMethodOptions<T> extends Select.SelectPassThroughMethodOptions<T> {}
|
||||||
/**
|
|
||||||
* Defines instance.
|
|
||||||
*/
|
|
||||||
instance: any;
|
|
||||||
/**
|
|
||||||
* Defines valid properties.
|
|
||||||
*/
|
|
||||||
props: DropdownProps;
|
|
||||||
/**
|
|
||||||
* Defines current inline state.
|
|
||||||
*/
|
|
||||||
state: DropdownState;
|
|
||||||
/**
|
|
||||||
* Defines parent instance.
|
|
||||||
*/
|
|
||||||
parent: T | any;
|
|
||||||
/**
|
|
||||||
* Defines current options.
|
|
||||||
*/
|
|
||||||
context: DropdownContext;
|
|
||||||
/**
|
|
||||||
* Defines passthrough(pt) options in global config.
|
|
||||||
*/
|
|
||||||
global: object | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom change event.
|
* Custom change event.
|
||||||
* @see {@link DropdownEmits.change}
|
* @see {@link DropdownEmits.change}
|
||||||
*/
|
*/
|
||||||
export interface DropdownChangeEvent {
|
export interface DropdownChangeEvent extends Select.SelectChangeEvent {}
|
||||||
/**
|
|
||||||
* Browser event.
|
|
||||||
*/
|
|
||||||
originalEvent: Event;
|
|
||||||
/**
|
|
||||||
* Selected option value
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom filter event.
|
* Custom filter event.
|
||||||
* @see {@link DropdownEmits.filter}
|
* @see {@link DropdownEmits.filter}
|
||||||
*/
|
*/
|
||||||
export interface DropdownFilterEvent {
|
export interface DropdownFilterEvent extends Select.SelectFilterEvent {}
|
||||||
/**
|
|
||||||
* Browser event.
|
|
||||||
*/
|
|
||||||
originalEvent: Event;
|
|
||||||
/**
|
|
||||||
* Filter value
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom passthrough(pt) options.
|
* Custom passthrough(pt) options.
|
||||||
* @see {@link DropdownProps.pt}
|
* @see {@link DropdownProps.pt}
|
||||||
*/
|
*/
|
||||||
export interface DropdownPassThroughOptions<T = any> {
|
export interface DropdownPassThroughOptions<T = any> extends Select.SelectPassThroughOptions<T> {}
|
||||||
/**
|
|
||||||
* Used to pass attributes to the root's DOM element.
|
|
||||||
*/
|
|
||||||
root?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the input's DOM element.
|
|
||||||
*/
|
|
||||||
input?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the clear icon's DOM element.
|
|
||||||
*/
|
|
||||||
clearIcon?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the trigger' DOM element.
|
|
||||||
*/
|
|
||||||
trigger?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the loading icon's DOM element.
|
|
||||||
*/
|
|
||||||
loadingIcon?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the panel's DOM element.
|
|
||||||
*/
|
|
||||||
panel?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the header's DOM element.
|
|
||||||
*/
|
|
||||||
header?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the filter container's DOM element.
|
|
||||||
*/
|
|
||||||
filterContainer?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the filter input's DOM element.
|
|
||||||
*/
|
|
||||||
filterInput?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the filter icon's DOM element.
|
|
||||||
*/
|
|
||||||
filterIcon?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the wrapper's DOM element.
|
|
||||||
*/
|
|
||||||
wrapper?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the VirtualScroller component.
|
|
||||||
* @see {@link VirtualScrollerPassThroughOptionType}
|
|
||||||
*/
|
|
||||||
virtualScroller?: VirtualScrollerPassThroughOptionType;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the list's DOM element.
|
|
||||||
*/
|
|
||||||
list?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the item group's DOM element.
|
|
||||||
*/
|
|
||||||
itemGroup?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the item group label's DOM element.
|
|
||||||
*/
|
|
||||||
itemGroupLabel?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the item's DOM element.
|
|
||||||
*/
|
|
||||||
item?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the item label's DOM element.
|
|
||||||
*/
|
|
||||||
itemLabel?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the check icon's DOM element.
|
|
||||||
*/
|
|
||||||
checkIcon?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the bank icon's DOM element.
|
|
||||||
*/
|
|
||||||
blankIcon?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the empty message's DOM element.
|
|
||||||
*/
|
|
||||||
emptyMessage?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the hidden first focusable element's DOM element.
|
|
||||||
*/
|
|
||||||
hiddenFirstFocusableEl?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the hidden filter result's DOM element.
|
|
||||||
*/
|
|
||||||
hiddenFilterResult?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the hidden empty message's DOM element.
|
|
||||||
*/
|
|
||||||
hiddenEmptyMessage?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the hidden selected message's DOM element.
|
|
||||||
*/
|
|
||||||
hiddenSelectedMessage?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to pass attributes to the hidden last focusable element's DOM element.
|
|
||||||
*/
|
|
||||||
hiddenLastFocusableEl?: DropdownPassThroughOptionType<T>;
|
|
||||||
/**
|
|
||||||
* Used to manage all lifecycle hooks.
|
|
||||||
* @see {@link BaseComponent.ComponentHooks}
|
|
||||||
*/
|
|
||||||
hooks?: ComponentHooks;
|
|
||||||
/**
|
|
||||||
* Used to control Vue Transition API.
|
|
||||||
*/
|
|
||||||
transition?: DropdownPassThroughTransitionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom passthrough attributes for each DOM elements
|
* Custom passthrough attributes for each DOM elements
|
||||||
*/
|
*/
|
||||||
export interface DropdownPassThroughAttributes {
|
export interface DropdownPassThroughAttributes extends Select.SelectPassThroughAttributes {}
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines current inline state in Dropdown component.
|
* Defines current inline state in Dropdown component.
|
||||||
*/
|
*/
|
||||||
export interface DropdownState {
|
export interface DropdownState extends Select.SelectState {}
|
||||||
/**
|
|
||||||
* Current id state as a string.
|
|
||||||
*/
|
|
||||||
id: string;
|
|
||||||
/**
|
|
||||||
* Current focused state as a boolean.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
focused: boolean;
|
|
||||||
/**
|
|
||||||
* Current focused item index as a number.
|
|
||||||
* @defaultValue -1
|
|
||||||
*/
|
|
||||||
focusedOptionIndex: number;
|
|
||||||
/**
|
|
||||||
* Current filter value state as a string.
|
|
||||||
*/
|
|
||||||
filterValue: string;
|
|
||||||
/**
|
|
||||||
* Current overlay visible state as a boolean.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
overlayVisible: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines current options in Dropdown component.
|
* Defines current options in Dropdown component.
|
||||||
*/
|
*/
|
||||||
export interface DropdownContext {
|
export interface DropdownContext extends Select.SelectContext {}
|
||||||
/**
|
|
||||||
* Current item option.
|
|
||||||
*/
|
|
||||||
option: any;
|
|
||||||
/**
|
|
||||||
* Current item index.
|
|
||||||
*/
|
|
||||||
index: number;
|
|
||||||
/**
|
|
||||||
* Current selection state of the item as a boolean.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
selected: boolean;
|
|
||||||
/**
|
|
||||||
* Current focus state of the item as a boolean.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
focused: boolean;
|
|
||||||
/**
|
|
||||||
* Current disabled state of the item as a boolean.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
disabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines valid properties in Dropdown component.
|
* Defines valid properties in Dropdown component.
|
||||||
*/
|
*/
|
||||||
export interface DropdownProps {
|
export interface DropdownProps extends Select.SelectProps {}
|
||||||
/**
|
|
||||||
* Value of the component.
|
|
||||||
*/
|
|
||||||
modelValue?: any;
|
|
||||||
/**
|
|
||||||
* An array of select items to display as the available options.
|
|
||||||
*/
|
|
||||||
options?: any[];
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the label of an option.
|
|
||||||
*/
|
|
||||||
optionLabel?: string | ((data: any) => string) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the value of an option, defaults to the option itself when not defined.
|
|
||||||
*/
|
|
||||||
optionValue?: string | ((data: any) => any) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the disabled flag of an option, defaults to false when not defined.
|
|
||||||
*/
|
|
||||||
optionDisabled?: string | ((data: any) => boolean) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function to use as the label of an option group.
|
|
||||||
*/
|
|
||||||
optionGroupLabel?: string | ((data: any) => string) | undefined;
|
|
||||||
/**
|
|
||||||
* Property name or getter function that refers to the children options of option group.
|
|
||||||
*/
|
|
||||||
optionGroupChildren?: string | ((data: any) => any[]) | undefined;
|
|
||||||
/**
|
|
||||||
* Height of the viewport, a scrollbar is defined if height of list exceeds this value.
|
|
||||||
* @defaultValue 14rem
|
|
||||||
*/
|
|
||||||
scrollHeight?: string | undefined;
|
|
||||||
/**
|
|
||||||
* When specified, displays a filter input at header.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
filter?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Placeholder text to show when filter input is empty.
|
|
||||||
*/
|
|
||||||
filterPlaceholder?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Locale to use in filtering. The default locale is the host environment's current locale.
|
|
||||||
*/
|
|
||||||
filterLocale?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines the filtering algorithm to use when searching the options.
|
|
||||||
* @defaultValue contains
|
|
||||||
*/
|
|
||||||
filterMatchMode?: HintedString<'contains' | 'startsWith' | 'endsWith'> | undefined;
|
|
||||||
/**
|
|
||||||
* Fields used when filtering the options, defaults to optionLabel.
|
|
||||||
*/
|
|
||||||
filterFields?: string[] | undefined;
|
|
||||||
/**
|
|
||||||
* When present, custom value instead of predefined options can be entered using the editable input field.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
editable?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Default text to display when no option is selected.
|
|
||||||
*/
|
|
||||||
placeholder?: 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 component should be disabled.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
disabled?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Specifies the input variant of the component.
|
|
||||||
* @defaultValue outlined
|
|
||||||
*/
|
|
||||||
variant?: 'outlined' | 'filled' | undefined;
|
|
||||||
/**
|
|
||||||
* A property to uniquely identify an option.
|
|
||||||
*/
|
|
||||||
dataKey?: string | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, a clear icon is displayed to clear the value.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
showClear?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying input element.
|
|
||||||
*/
|
|
||||||
inputId?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the input field.
|
|
||||||
*/
|
|
||||||
inputStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the input field.
|
|
||||||
*/
|
|
||||||
inputClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* Inline style of the overlay panel.
|
|
||||||
*/
|
|
||||||
panelStyle?: object | undefined;
|
|
||||||
/**
|
|
||||||
* Style class of the overlay panel.
|
|
||||||
*/
|
|
||||||
panelClass?: string | object | undefined;
|
|
||||||
/**
|
|
||||||
* A valid query selector or an HTMLElement to specify where the overlay gets attached.
|
|
||||||
* @defaultValue body
|
|
||||||
*/
|
|
||||||
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
|
||||||
/**
|
|
||||||
* Whether the dropdown is in loading state.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
loading?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in clear button.
|
|
||||||
* @deprecated since v3.27.0. Use 'clearicon' slot.
|
|
||||||
*/
|
|
||||||
clearIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in the dropdown.
|
|
||||||
* @deprecated since v3.27.0. Use 'dropdownicon' slot.
|
|
||||||
*/
|
|
||||||
dropdownIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in filter input.
|
|
||||||
* @deprecated since v3.27.0. Use 'filtericon' slot.
|
|
||||||
*/
|
|
||||||
filterIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Icon to display in loading state.
|
|
||||||
* @deprecated since v3.27.0. Use 'loadingicon' slot.
|
|
||||||
*/
|
|
||||||
loadingIcon?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Clears the filter value when hiding the dropdown.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
resetFilterOnHide?: boolean;
|
|
||||||
/**
|
|
||||||
* Clears the filter value when clicking on the clear icon.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
resetFilterOnClear?: boolean;
|
|
||||||
/**
|
|
||||||
* Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it.
|
|
||||||
*/
|
|
||||||
virtualScrollerOptions?: VirtualScrollerProps;
|
|
||||||
/**
|
|
||||||
* Whether to focus on the first visible or selected element when the overlay panel is shown.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
autoOptionFocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether to focus on the filter element when the overlay panel is shown.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
autoFilterFocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, the focused option is selected.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
selectOnFocus?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* When enabled, the focus is placed on the hovered option.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
focusOnHover?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether the selected option will be add highlight class.
|
|
||||||
* @defaultValue true
|
|
||||||
*/
|
|
||||||
highlightOnSelect?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Whether the selected option will be shown with a check mark.
|
|
||||||
* @defaultValue false
|
|
||||||
*/
|
|
||||||
checkmark?: boolean | undefined;
|
|
||||||
/**
|
|
||||||
* Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue '{0} results are available'
|
|
||||||
*/
|
|
||||||
filterMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue '{0} items selected'
|
|
||||||
*/
|
|
||||||
selectionMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue No selected item
|
|
||||||
*/
|
|
||||||
emptySelectionMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue No results found
|
|
||||||
*/
|
|
||||||
emptyFilterMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Text to display when there are no options available. Defaults to value from PrimeVue locale configuration.
|
|
||||||
* @defaultValue No results found
|
|
||||||
*/
|
|
||||||
emptyMessage?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Index of the element in tabbing order.
|
|
||||||
*/
|
|
||||||
tabindex?: number | string | undefined;
|
|
||||||
/**
|
|
||||||
* Defines a string value that labels an interactive element.
|
|
||||||
*/
|
|
||||||
ariaLabel?: string | undefined;
|
|
||||||
/**
|
|
||||||
* Identifier of the underlying input element.
|
|
||||||
*/
|
|
||||||
ariaLabelledby?: 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 {DropdownPassThroughOptions}
|
|
||||||
*/
|
|
||||||
pt?: PassThrough<DropdownPassThroughOptions>;
|
|
||||||
/**
|
|
||||||
* 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 Dropdown component.
|
* Defines valid slots in Dropdown component.
|
||||||
*/
|
*/
|
||||||
export interface DropdownSlots {
|
export interface DropdownSlots extends Select.SelectSlots {}
|
||||||
/**
|
|
||||||
* Custom value template.
|
|
||||||
* @param {Object} scope - value slot's params.
|
|
||||||
*/
|
|
||||||
value(scope: {
|
|
||||||
/**
|
|
||||||
* Value of the component
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
/**
|
|
||||||
* Placeholder prop value
|
|
||||||
*/
|
|
||||||
placeholder: string;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom indicator template.
|
|
||||||
* @deprecated since v3.27.0. Use 'dropdownicon or loadingicon' slots.
|
|
||||||
*/
|
|
||||||
indicator(): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom header template of panel.
|
|
||||||
* @param {Object} scope - header slot's params.
|
|
||||||
*/
|
|
||||||
header(scope: {
|
|
||||||
/**
|
|
||||||
* Value of the component
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
/**
|
|
||||||
* Displayed options
|
|
||||||
*/
|
|
||||||
options: any[];
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom footer template of panel.
|
|
||||||
* @param {Object} scope - footer slot's params.
|
|
||||||
*/
|
|
||||||
footer(scope: {
|
|
||||||
/**
|
|
||||||
* Value of the component
|
|
||||||
*/
|
|
||||||
value: any;
|
|
||||||
/**
|
|
||||||
* Displayed options
|
|
||||||
*/
|
|
||||||
options: any[];
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom option template.
|
|
||||||
* @param {Object} scope - option slot's params.
|
|
||||||
*/
|
|
||||||
option(scope: {
|
|
||||||
/**
|
|
||||||
* Option instance
|
|
||||||
*/
|
|
||||||
option: any;
|
|
||||||
/**
|
|
||||||
* Index of the option
|
|
||||||
*/
|
|
||||||
index: number;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom option group template.
|
|
||||||
* @param {Object} scope - option group slot's params.
|
|
||||||
*/
|
|
||||||
optiongroup(scope: {
|
|
||||||
/**
|
|
||||||
* Option instance
|
|
||||||
*/
|
|
||||||
option: any;
|
|
||||||
/**
|
|
||||||
* Index of the option
|
|
||||||
*/
|
|
||||||
index: number;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom empty filter template.
|
|
||||||
*/
|
|
||||||
emptyfilter(): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom empty template.
|
|
||||||
*/
|
|
||||||
empty(): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom content template.
|
|
||||||
* @param {Object} scope - content slot's params.
|
|
||||||
*/
|
|
||||||
content(scope: {
|
|
||||||
/**
|
|
||||||
* An array of objects to display for virtualscroller
|
|
||||||
*/
|
|
||||||
items: any;
|
|
||||||
/**
|
|
||||||
* Style class of the component
|
|
||||||
*/
|
|
||||||
styleClass: string;
|
|
||||||
/**
|
|
||||||
* Referance of the content
|
|
||||||
* @param {HTMLElement} el - Element of 'ref' property
|
|
||||||
*/
|
|
||||||
contentRef: (el: any) => void;
|
|
||||||
/**
|
|
||||||
* Options of the items
|
|
||||||
* @param {number} index - Rendered index
|
|
||||||
* @return {@link VirtualScrollerItemOptions}
|
|
||||||
*/
|
|
||||||
getItemOptions: (index: number) => VirtualScrollerItemOptions;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom loader template.
|
|
||||||
* @param {Object} scope - loader slot's params.
|
|
||||||
*/
|
|
||||||
loader(scope: {
|
|
||||||
/**
|
|
||||||
* Options of the loader items for virtualscroller
|
|
||||||
*/
|
|
||||||
options: any[];
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom clear icon template.
|
|
||||||
* @param {Object} scope - clear icon slot's params.
|
|
||||||
*/
|
|
||||||
clearicon(scope: {
|
|
||||||
/**
|
|
||||||
* Style class of the clear icon
|
|
||||||
*/
|
|
||||||
class: any;
|
|
||||||
/**
|
|
||||||
* Clear icon click function.
|
|
||||||
* @param {Event} event - Browser event
|
|
||||||
* @deprecated since v3.39.0. Use 'clearCallback' property instead.
|
|
||||||
*/
|
|
||||||
onClick: (event: Event) => void;
|
|
||||||
/**
|
|
||||||
* Clear icon click function.
|
|
||||||
* @param {Event} event - Browser event
|
|
||||||
*/
|
|
||||||
clearCallback: (event: Event) => void;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom dropdown icon template.
|
|
||||||
* @param {Object} scope - dropdown icon slot's params.
|
|
||||||
*/
|
|
||||||
dropdownicon(scope: {
|
|
||||||
/**
|
|
||||||
* Style class of the dropdown icon
|
|
||||||
*/
|
|
||||||
class: any;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom loading icon template.
|
|
||||||
* @param {Object} scope - loading icon slot's params.
|
|
||||||
*/
|
|
||||||
loadingicon(scope: {
|
|
||||||
/**
|
|
||||||
* Style class of the loading icon
|
|
||||||
*/
|
|
||||||
class: any;
|
|
||||||
}): VNode[];
|
|
||||||
/**
|
|
||||||
* Custom filter icon template.
|
|
||||||
* @param {Object} scope - filter icon slot's params.
|
|
||||||
*/
|
|
||||||
filtericon(scope: {
|
|
||||||
/**
|
|
||||||
* Style class of the filter icon
|
|
||||||
*/
|
|
||||||
class: any;
|
|
||||||
}): VNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines valid emits in Dropdown component.
|
* Defines valid emits in Dropdown component.
|
||||||
*/
|
*/
|
||||||
export interface DropdownEmits {
|
export interface DropdownEmits extends Select.SelectEmits {}
|
||||||
/**
|
|
||||||
* Emitted when the value changes.
|
|
||||||
* @param {*} value - New value.
|
|
||||||
*/
|
|
||||||
'update:modelValue'(value: any): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke on value change.
|
|
||||||
* @param {DropdownChangeEvent} event - Custom change event.
|
|
||||||
*/
|
|
||||||
change(event: DropdownChangeEvent): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke when the component receives focus.
|
|
||||||
* @param {Event} event - Browser event.
|
|
||||||
*/
|
|
||||||
focus(event: Event): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke when the component loses focus.
|
|
||||||
* @param {Event} event - Browser event.
|
|
||||||
*/
|
|
||||||
blur(event: Event): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke before the overlay is shown.
|
|
||||||
*/
|
|
||||||
'before-show'(): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke before the overlay is hidden.
|
|
||||||
*/
|
|
||||||
'before-hide'(): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke when the overlay is shown.
|
|
||||||
*/
|
|
||||||
show(): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke when the overlay is hidden.
|
|
||||||
*/
|
|
||||||
hide(): void;
|
|
||||||
/**
|
|
||||||
* Callback to invoke on filter input.
|
|
||||||
* @param {DropdownFilterEvent} event - Custom filter event.
|
|
||||||
*/
|
|
||||||
filter(event: DropdownFilterEvent): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated Deprecated since v4. Use Select component instead.
|
||||||
|
*
|
||||||
* **PrimeVue - Dropdown**
|
* **PrimeVue - Dropdown**
|
||||||
*
|
*
|
||||||
* _Dropdown also known as Select, is used to choose an item from a collection of options._
|
* _Dropdown also known as Select, is used to choose an item from a collection of options._
|
||||||
*
|
*
|
||||||
* [Live Demo](https://www.primevue.org/dropdown/)
|
* [Live Demo](https://www.primevue.org/select/)
|
||||||
* --- ---
|
* --- ---
|
||||||
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
|
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,991 +1,11 @@
|
||||||
<template>
|
|
||||||
<div ref="container" :id="id" :class="cx('root')" @click="onContainerClick" v-bind="ptmi('root')">
|
|
||||||
<input
|
|
||||||
v-if="editable"
|
|
||||||
ref="focusInput"
|
|
||||||
:id="inputId"
|
|
||||||
type="text"
|
|
||||||
:class="[cx('input'), inputClass]"
|
|
||||||
:style="inputStyle"
|
|
||||||
:value="editableInputValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:tabindex="!disabled ? tabindex : -1"
|
|
||||||
:disabled="disabled"
|
|
||||||
autocomplete="off"
|
|
||||||
role="combobox"
|
|
||||||
:aria-label="ariaLabel"
|
|
||||||
:aria-labelledby="ariaLabelledby"
|
|
||||||
aria-haspopup="listbox"
|
|
||||||
:aria-expanded="overlayVisible"
|
|
||||||
:aria-controls="id + '_list'"
|
|
||||||
:aria-activedescendant="focused ? focusedOptionId : undefined"
|
|
||||||
:aria-invalid="invalid || undefined"
|
|
||||||
@focus="onFocus"
|
|
||||||
@blur="onBlur"
|
|
||||||
@keydown="onKeyDown"
|
|
||||||
@input="onEditableInput"
|
|
||||||
v-bind="ptm('input')"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
ref="focusInput"
|
|
||||||
:id="inputId"
|
|
||||||
:class="[cx('input'), inputClass]"
|
|
||||||
:style="inputStyle"
|
|
||||||
:tabindex="!disabled ? tabindex : -1"
|
|
||||||
role="combobox"
|
|
||||||
:aria-label="ariaLabel || (label === 'p-emptylabel' ? undefined : label)"
|
|
||||||
:aria-labelledby="ariaLabelledby"
|
|
||||||
aria-haspopup="listbox"
|
|
||||||
:aria-expanded="overlayVisible"
|
|
||||||
:aria-controls="id + '_list'"
|
|
||||||
:aria-activedescendant="focused ? focusedOptionId : undefined"
|
|
||||||
:aria-disabled="disabled"
|
|
||||||
@focus="onFocus"
|
|
||||||
@blur="onBlur"
|
|
||||||
@keydown="onKeyDown"
|
|
||||||
v-bind="ptm('input')"
|
|
||||||
>
|
|
||||||
<slot name="value" :value="modelValue" :placeholder="placeholder">{{ label === 'p-emptylabel' ? ' ' : label || 'empty' }}</slot>
|
|
||||||
</span>
|
|
||||||
<slot v-if="showClear && modelValue != null" name="clearicon" :class="cx('clearIcon')" :onClick="onClearClick" :clearCallback="onClearClick">
|
|
||||||
<component :is="clearIcon ? 'i' : 'TimesIcon'" ref="clearIcon" :class="[cx('clearIcon'), clearIcon]" @click="onClearClick" v-bind="ptm('clearIcon')" data-pc-section="clearicon" />
|
|
||||||
</slot>
|
|
||||||
<div :class="cx('trigger')" v-bind="ptm('trigger')">
|
|
||||||
<slot v-if="loading" name="loadingicon" :class="cx('loadingIcon')">
|
|
||||||
<span v-if="loadingIcon" :class="[cx('loadingIcon'), 'pi-spin', loadingIcon]" aria-hidden="true" v-bind="ptm('loadingIcon')" />
|
|
||||||
<SpinnerIcon v-else :class="cx('loadingIcon')" spin aria-hidden="true" v-bind="ptm('loadingIcon')" />
|
|
||||||
</slot>
|
|
||||||
<slot v-else name="dropdownicon" :class="cx('dropdownIcon')">
|
|
||||||
<component :is="dropdownIcon ? 'span' : 'ChevronDownIcon'" :class="[cx('dropdownIcon'), dropdownIcon]" aria-hidden="true" v-bind="ptm('dropdownIcon')" />
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
<Portal :appendTo="appendTo">
|
|
||||||
<transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave" v-bind="ptm('transition')">
|
|
||||||
<div v-if="overlayVisible" :ref="overlayRef" :class="[cx('panel'), panelClass]" :style="panelStyle" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="ptm('panel')">
|
|
||||||
<span
|
|
||||||
ref="firstHiddenFocusableElementOnOverlay"
|
|
||||||
role="presentation"
|
|
||||||
aria-hidden="true"
|
|
||||||
class="p-hidden-accessible p-hidden-focusable"
|
|
||||||
:tabindex="0"
|
|
||||||
@focus="onFirstHiddenFocus"
|
|
||||||
v-bind="ptm('hiddenFirstFocusableEl')"
|
|
||||||
:data-p-hidden-accessible="true"
|
|
||||||
:data-p-hidden-focusable="true"
|
|
||||||
></span>
|
|
||||||
<slot name="header" :value="modelValue" :options="visibleOptions"></slot>
|
|
||||||
<div v-if="filter" :class="cx('header')" v-bind="ptm('header')">
|
|
||||||
<div :class="cx('filterContainer')" v-bind="ptm('filterContainer')">
|
|
||||||
<input
|
|
||||||
ref="filterInput"
|
|
||||||
type="text"
|
|
||||||
:value="filterValue"
|
|
||||||
@vue:mounted="onFilterUpdated"
|
|
||||||
@vue:updated="onFilterUpdated"
|
|
||||||
:class="cx('filterInput')"
|
|
||||||
:placeholder="filterPlaceholder"
|
|
||||||
role="searchbox"
|
|
||||||
autocomplete="off"
|
|
||||||
:aria-owns="id + '_list'"
|
|
||||||
:aria-activedescendant="focusedOptionId"
|
|
||||||
@keydown="onFilterKeyDown"
|
|
||||||
@blur="onFilterBlur"
|
|
||||||
@input="onFilterChange"
|
|
||||||
v-bind="ptm('filterInput')"
|
|
||||||
/>
|
|
||||||
<slot name="filtericon" :class="cx('filterIcon')">
|
|
||||||
<component :is="filterIcon ? 'span' : 'SearchIcon'" :class="[cx('filterIcon'), filterIcon]" v-bind="ptm('filterIcon')" />
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
<span role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenFilterResult')" :data-p-hidden-accessible="true">
|
|
||||||
{{ filterResultMessageText }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div :class="cx('wrapper')" :style="{ 'max-height': virtualScrollerDisabled ? scrollHeight : '' }" v-bind="ptm('wrapper')">
|
|
||||||
<VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :items="visibleOptions" :style="{ height: scrollHeight }" :tabindex="-1" :disabled="virtualScrollerDisabled" :pt="ptm('virtualScroller')">
|
|
||||||
<template v-slot:content="{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }">
|
|
||||||
<ul :ref="(el) => listRef(el, contentRef)" :id="id + '_list'" :class="[cx('list'), styleClass]" :style="contentStyle" role="listbox" v-bind="ptm('list')">
|
|
||||||
<template v-for="(option, i) of items" :key="getOptionRenderKey(option, getOptionIndex(i, getItemOptions))">
|
|
||||||
<li v-if="isOptionGroup(option)" :id="id + '_' + getOptionIndex(i, getItemOptions)" :style="{ height: itemSize ? itemSize + 'px' : undefined }" :class="cx('itemGroup')" role="option" v-bind="ptm('itemGroup')">
|
|
||||||
<slot name="optiongroup" :option="option.optionGroup" :index="getOptionIndex(i, getItemOptions)">
|
|
||||||
<span :class="cx('itemGroupLabel')" v-bind="ptm('itemGroupLabel')">{{ getOptionGroupLabel(option.optionGroup) }}</span>
|
|
||||||
</slot>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-else
|
|
||||||
:id="id + '_' + getOptionIndex(i, getItemOptions)"
|
|
||||||
v-ripple
|
|
||||||
:class="cx('item', { option, focusedOption: getOptionIndex(i, getItemOptions) })"
|
|
||||||
:style="{ height: itemSize ? itemSize + 'px' : undefined }"
|
|
||||||
role="option"
|
|
||||||
:aria-label="getOptionLabel(option)"
|
|
||||||
:aria-selected="isSelected(option)"
|
|
||||||
:aria-disabled="isOptionDisabled(option)"
|
|
||||||
:aria-setsize="ariaSetSize"
|
|
||||||
:aria-posinset="getAriaPosInset(getOptionIndex(i, getItemOptions))"
|
|
||||||
@click="onOptionSelect($event, option)"
|
|
||||||
@mousemove="onOptionMouseMove($event, getOptionIndex(i, getItemOptions))"
|
|
||||||
:data-p-highlight="isSelected(option)"
|
|
||||||
:data-p-focused="focusedOptionIndex === getOptionIndex(i, getItemOptions)"
|
|
||||||
:data-p-disabled="isOptionDisabled(option)"
|
|
||||||
v-bind="getPTItemOptions(option, getItemOptions, i, 'item')"
|
|
||||||
>
|
|
||||||
<template v-if="checkmark">
|
|
||||||
<CheckIcon v-if="isSelected(option)" :class="cx('checkIcon')" v-bind="ptm('checkIcon')" />
|
|
||||||
<BlankIcon v-else :class="cx('blankIcon')" v-bind="ptm('blankIcon')" />
|
|
||||||
</template>
|
|
||||||
<slot name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">
|
|
||||||
<span :class="cx('itemLabel')" v-bind="ptm('itemLabel')">{{ getOptionLabel(option) }}</span>
|
|
||||||
</slot>
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
<li v-if="filterValue && (!items || (items && items.length === 0))" :class="cx('emptyMessage')" role="option" v-bind="ptm('emptyMessage')" :data-p-hidden-accessible="true">
|
|
||||||
<slot name="emptyfilter">{{ emptyFilterMessageText }}</slot>
|
|
||||||
</li>
|
|
||||||
<li v-else-if="!options || (options && options.length === 0)" :class="cx('emptyMessage')" role="option" v-bind="ptm('emptyMessage')" :data-p-hidden-accessible="true">
|
|
||||||
<slot name="empty">{{ emptyMessageText }}</slot>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<template v-if="$slots.loader" v-slot:loader="{ options }">
|
|
||||||
<slot name="loader" :options="options"></slot>
|
|
||||||
</template>
|
|
||||||
</VirtualScroller>
|
|
||||||
</div>
|
|
||||||
<slot name="footer" :value="modelValue" :options="visibleOptions"></slot>
|
|
||||||
<span v-if="!options || (options && options.length === 0)" role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenEmptyMessage')" :data-p-hidden-accessible="true">
|
|
||||||
{{ emptyMessageText }}
|
|
||||||
</span>
|
|
||||||
<span role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenSelectedMessage')" :data-p-hidden-accessible="true">
|
|
||||||
{{ selectedMessageText }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
ref="lastHiddenFocusableElementOnOverlay"
|
|
||||||
role="presentation"
|
|
||||||
aria-hidden="true"
|
|
||||||
class="p-hidden-accessible p-hidden-focusable"
|
|
||||||
:tabindex="0"
|
|
||||||
@focus="onLastHiddenFocus"
|
|
||||||
v-bind="ptm('hiddenLastFocusableEl')"
|
|
||||||
:data-p-hidden-accessible="true"
|
|
||||||
:data-p-hidden-focusable="true"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</Portal>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { FilterService } from 'primevue/api';
|
import Select from 'primevue/select';
|
||||||
import BlankIcon from 'primevue/icons/blank';
|
|
||||||
import CheckIcon from 'primevue/icons/check';
|
|
||||||
import ChevronDownIcon from 'primevue/icons/chevrondown';
|
|
||||||
import SearchIcon from 'primevue/icons/search';
|
|
||||||
import SpinnerIcon from 'primevue/icons/spinner';
|
|
||||||
import TimesIcon from 'primevue/icons/times';
|
|
||||||
import OverlayEventBus from 'primevue/overlayeventbus';
|
|
||||||
import Portal from 'primevue/portal';
|
|
||||||
import Ripple from 'primevue/ripple';
|
|
||||||
import { ConnectedOverlayScrollHandler, DomHandler, ObjectUtils, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
|
|
||||||
import VirtualScroller from 'primevue/virtualscroller';
|
|
||||||
import BaseDropdown from './BaseDropdown.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Dropdown',
|
name: 'Dropdown',
|
||||||
extends: BaseDropdown,
|
extends: Select,
|
||||||
inheritAttrs: false,
|
|
||||||
emits: ['update:modelValue', 'change', 'focus', 'blur', 'before-show', 'before-hide', 'show', 'hide', 'filter'],
|
|
||||||
outsideClickListener: null,
|
|
||||||
scrollHandler: null,
|
|
||||||
resizeListener: null,
|
|
||||||
labelClickListener: null,
|
|
||||||
overlay: null,
|
|
||||||
list: null,
|
|
||||||
virtualScroller: null,
|
|
||||||
searchTimeout: null,
|
|
||||||
searchValue: null,
|
|
||||||
isModelValueChanged: false,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
id: this.$attrs.id,
|
|
||||||
clicked: false,
|
|
||||||
focused: false,
|
|
||||||
focusedOptionIndex: -1,
|
|
||||||
filterValue: null,
|
|
||||||
overlayVisible: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$attrs.id': function (newValue) {
|
|
||||||
this.id = newValue || UniqueComponentId();
|
|
||||||
},
|
|
||||||
modelValue() {
|
|
||||||
this.isModelValueChanged = true;
|
|
||||||
},
|
|
||||||
options() {
|
|
||||||
this.autoUpdateModel();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.id = this.id || UniqueComponentId();
|
console.warn('Deprecated since v4. Use Select component instead.');
|
||||||
this.autoUpdateModel();
|
|
||||||
this.bindLabelClickListener();
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
if (this.overlayVisible && this.isModelValueChanged) {
|
|
||||||
this.scrollInView(this.findSelectedOptionIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isModelValueChanged = false;
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
this.unbindOutsideClickListener();
|
|
||||||
this.unbindResizeListener();
|
|
||||||
this.unbindLabelClickListener();
|
|
||||||
|
|
||||||
if (this.scrollHandler) {
|
|
||||||
this.scrollHandler.destroy();
|
|
||||||
this.scrollHandler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.overlay) {
|
|
||||||
ZIndexUtils.clear(this.overlay);
|
|
||||||
this.overlay = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getOptionIndex(index, fn) {
|
|
||||||
return this.virtualScrollerDisabled ? index : fn && fn(index)['index'];
|
|
||||||
},
|
|
||||||
getOptionLabel(option) {
|
|
||||||
return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option;
|
|
||||||
},
|
|
||||||
getOptionValue(option) {
|
|
||||||
return this.optionValue ? ObjectUtils.resolveFieldData(option, this.optionValue) : option;
|
|
||||||
},
|
|
||||||
getOptionRenderKey(option, index) {
|
|
||||||
return (this.dataKey ? ObjectUtils.resolveFieldData(option, this.dataKey) : this.getOptionLabel(option)) + '_' + index;
|
|
||||||
},
|
|
||||||
getPTItemOptions(option, itemOptions, index, key) {
|
|
||||||
return this.ptm(key, {
|
|
||||||
context: {
|
|
||||||
option,
|
|
||||||
index,
|
|
||||||
selected: this.isSelected(option),
|
|
||||||
focused: this.focusedOptionIndex === this.getOptionIndex(index, itemOptions),
|
|
||||||
disabled: this.isOptionDisabled(option)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
isOptionDisabled(option) {
|
|
||||||
return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
|
|
||||||
},
|
|
||||||
isOptionGroup(option) {
|
|
||||||
return this.optionGroupLabel && option.optionGroup && option.group;
|
|
||||||
},
|
|
||||||
getOptionGroupLabel(optionGroup) {
|
|
||||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel);
|
|
||||||
},
|
|
||||||
getOptionGroupChildren(optionGroup) {
|
|
||||||
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren);
|
|
||||||
},
|
|
||||||
getAriaPosInset(index) {
|
|
||||||
return (this.optionGroupLabel ? index - this.visibleOptions.slice(0, index).filter((option) => this.isOptionGroup(option)).length : index) + 1;
|
|
||||||
},
|
|
||||||
show(isFocus) {
|
|
||||||
this.$emit('before-show');
|
|
||||||
this.overlayVisible = true;
|
|
||||||
this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.editable ? -1 : this.findSelectedOptionIndex();
|
|
||||||
|
|
||||||
isFocus && DomHandler.focus(this.$refs.focusInput);
|
|
||||||
},
|
|
||||||
hide(isFocus) {
|
|
||||||
const _hide = () => {
|
|
||||||
this.$emit('before-hide');
|
|
||||||
this.overlayVisible = false;
|
|
||||||
this.clicked = false;
|
|
||||||
this.focusedOptionIndex = -1;
|
|
||||||
this.searchValue = '';
|
|
||||||
|
|
||||||
this.resetFilterOnHide && (this.filterValue = null);
|
|
||||||
isFocus && DomHandler.focus(this.$refs.focusInput);
|
|
||||||
};
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
_hide();
|
|
||||||
}, 0); // For ScreenReaders
|
|
||||||
},
|
|
||||||
onFocus(event) {
|
|
||||||
if (this.disabled) {
|
|
||||||
// For ScreenReaders
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.focused = true;
|
|
||||||
|
|
||||||
if (this.overlayVisible) {
|
|
||||||
this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.editable ? -1 : this.findSelectedOptionIndex();
|
|
||||||
this.scrollInView(this.focusedOptionIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('focus', event);
|
|
||||||
},
|
|
||||||
onBlur(event) {
|
|
||||||
this.focused = false;
|
|
||||||
this.focusedOptionIndex = -1;
|
|
||||||
this.searchValue = '';
|
|
||||||
this.$emit('blur', event);
|
|
||||||
},
|
|
||||||
onKeyDown(event) {
|
|
||||||
if (this.disabled || DomHandler.isAndroid()) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metaKey = event.metaKey || event.ctrlKey;
|
|
||||||
|
|
||||||
switch (event.code) {
|
|
||||||
case 'ArrowDown':
|
|
||||||
this.onArrowDownKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowUp':
|
|
||||||
this.onArrowUpKey(event, this.editable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowLeft':
|
|
||||||
case 'ArrowRight':
|
|
||||||
this.onArrowLeftKey(event, this.editable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Delete':
|
|
||||||
this.onDeleteKey(event);
|
|
||||||
|
|
||||||
case 'Home':
|
|
||||||
this.onHomeKey(event, this.editable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'End':
|
|
||||||
this.onEndKey(event, this.editable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'PageDown':
|
|
||||||
this.onPageDownKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'PageUp':
|
|
||||||
this.onPageUpKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Space':
|
|
||||||
this.onSpaceKey(event, this.editable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Enter':
|
|
||||||
case 'NumpadEnter':
|
|
||||||
this.onEnterKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Escape':
|
|
||||||
this.onEscapeKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Tab':
|
|
||||||
this.onTabKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Backspace':
|
|
||||||
this.onBackspaceKey(event, this.editable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ShiftLeft':
|
|
||||||
case 'ShiftRight':
|
|
||||||
//NOOP
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (!metaKey && ObjectUtils.isPrintableCharacter(event.key)) {
|
|
||||||
!this.overlayVisible && this.show();
|
|
||||||
!this.editable && this.searchOptions(event, event.key);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clicked = false;
|
|
||||||
},
|
|
||||||
onEditableInput(event) {
|
|
||||||
const value = event.target.value;
|
|
||||||
|
|
||||||
this.searchValue = '';
|
|
||||||
const matched = this.searchOptions(event, value);
|
|
||||||
|
|
||||||
!matched && (this.focusedOptionIndex = -1);
|
|
||||||
|
|
||||||
this.updateModel(event, value);
|
|
||||||
|
|
||||||
!this.overlayVisible && ObjectUtils.isNotEmpty(value) && this.show();
|
|
||||||
},
|
|
||||||
onContainerClick(event) {
|
|
||||||
if (this.disabled || this.loading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.target.tagName === 'INPUT' || event.target.getAttribute('data-pc-section') === 'clearicon' || event.target.closest('[data-pc-section="clearicon"]')) {
|
|
||||||
return;
|
|
||||||
} else if (!this.overlay || !this.overlay.contains(event.target)) {
|
|
||||||
this.overlayVisible ? this.hide(true) : this.show(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clicked = true;
|
|
||||||
},
|
|
||||||
onClearClick(event) {
|
|
||||||
this.updateModel(event, null);
|
|
||||||
this.resetFilterOnClear && (this.filterValue = null);
|
|
||||||
},
|
|
||||||
onFirstHiddenFocus(event) {
|
|
||||||
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getFirstFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
|
|
||||||
|
|
||||||
DomHandler.focus(focusableEl);
|
|
||||||
},
|
|
||||||
onLastHiddenFocus(event) {
|
|
||||||
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getLastFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
|
|
||||||
|
|
||||||
DomHandler.focus(focusableEl);
|
|
||||||
},
|
|
||||||
onOptionSelect(event, option, isHide = true) {
|
|
||||||
const value = this.getOptionValue(option);
|
|
||||||
|
|
||||||
this.updateModel(event, value);
|
|
||||||
isHide && this.hide(true);
|
|
||||||
},
|
|
||||||
onOptionMouseMove(event, index) {
|
|
||||||
if (this.focusOnHover) {
|
|
||||||
this.changeFocusedOptionIndex(event, index);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onFilterChange(event) {
|
|
||||||
const value = event.target.value;
|
|
||||||
|
|
||||||
this.filterValue = value;
|
|
||||||
this.focusedOptionIndex = -1;
|
|
||||||
this.$emit('filter', { originalEvent: event, value });
|
|
||||||
|
|
||||||
!this.virtualScrollerDisabled && this.virtualScroller.scrollToIndex(0);
|
|
||||||
},
|
|
||||||
onFilterKeyDown(event) {
|
|
||||||
switch (event.code) {
|
|
||||||
case 'ArrowDown':
|
|
||||||
this.onArrowDownKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowUp':
|
|
||||||
this.onArrowUpKey(event, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ArrowLeft':
|
|
||||||
case 'ArrowRight':
|
|
||||||
this.onArrowLeftKey(event, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Home':
|
|
||||||
this.onHomeKey(event, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'End':
|
|
||||||
this.onEndKey(event, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Enter':
|
|
||||||
case 'NumpadEnter':
|
|
||||||
this.onEnterKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Escape':
|
|
||||||
this.onEscapeKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'Tab':
|
|
||||||
this.onTabKey(event, true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onFilterBlur() {
|
|
||||||
this.focusedOptionIndex = -1;
|
|
||||||
},
|
|
||||||
onFilterUpdated() {
|
|
||||||
if (this.overlayVisible) {
|
|
||||||
this.alignOverlay();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onOverlayClick(event) {
|
|
||||||
OverlayEventBus.emit('overlay-click', {
|
|
||||||
originalEvent: event,
|
|
||||||
target: this.$el
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onOverlayKeyDown(event) {
|
|
||||||
switch (event.code) {
|
|
||||||
case 'Escape':
|
|
||||||
this.onEscapeKey(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDeleteKey(event) {
|
|
||||||
if (this.showClear) {
|
|
||||||
this.updateModel(event, null);
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onArrowDownKey(event) {
|
|
||||||
if (!this.overlayVisible) {
|
|
||||||
this.show();
|
|
||||||
this.editable && this.changeFocusedOptionIndex(event, this.findSelectedOptionIndex());
|
|
||||||
} else {
|
|
||||||
const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();
|
|
||||||
|
|
||||||
this.changeFocusedOptionIndex(event, optionIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
onArrowUpKey(event, pressedInInputText = false) {
|
|
||||||
if (event.altKey && !pressedInInputText) {
|
|
||||||
if (this.focusedOptionIndex !== -1) {
|
|
||||||
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.overlayVisible && this.hide();
|
|
||||||
event.preventDefault();
|
|
||||||
} else {
|
|
||||||
const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();
|
|
||||||
|
|
||||||
this.changeFocusedOptionIndex(event, optionIndex);
|
|
||||||
|
|
||||||
!this.overlayVisible && this.show();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onArrowLeftKey(event, pressedInInputText = false) {
|
|
||||||
pressedInInputText && (this.focusedOptionIndex = -1);
|
|
||||||
},
|
|
||||||
onHomeKey(event, pressedInInputText = false) {
|
|
||||||
if (pressedInInputText) {
|
|
||||||
event.currentTarget.setSelectionRange(0, 0);
|
|
||||||
this.focusedOptionIndex = -1;
|
|
||||||
} else {
|
|
||||||
this.changeFocusedOptionIndex(event, this.findFirstOptionIndex());
|
|
||||||
|
|
||||||
!this.overlayVisible && this.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
onEndKey(event, pressedInInputText = false) {
|
|
||||||
if (pressedInInputText) {
|
|
||||||
const target = event.currentTarget;
|
|
||||||
const len = target.value.length;
|
|
||||||
|
|
||||||
target.setSelectionRange(len, len);
|
|
||||||
this.focusedOptionIndex = -1;
|
|
||||||
} else {
|
|
||||||
this.changeFocusedOptionIndex(event, this.findLastOptionIndex());
|
|
||||||
|
|
||||||
!this.overlayVisible && this.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
onPageUpKey(event) {
|
|
||||||
this.scrollInView(0);
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
onPageDownKey(event) {
|
|
||||||
this.scrollInView(this.visibleOptions.length - 1);
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
onEnterKey(event) {
|
|
||||||
if (!this.overlayVisible) {
|
|
||||||
this.focusedOptionIndex = -1; // reset
|
|
||||||
this.onArrowDownKey(event);
|
|
||||||
} else {
|
|
||||||
if (this.focusedOptionIndex !== -1) {
|
|
||||||
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
},
|
|
||||||
onSpaceKey(event, pressedInInputText = false) {
|
|
||||||
!pressedInInputText && this.onEnterKey(event);
|
|
||||||
},
|
|
||||||
onEscapeKey(event) {
|
|
||||||
this.overlayVisible && this.hide(true);
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation(); //@todo will be changed next versionss
|
|
||||||
},
|
|
||||||
onTabKey(event, pressedInInputText = false) {
|
|
||||||
if (!pressedInInputText) {
|
|
||||||
if (this.overlayVisible && this.hasFocusableElements()) {
|
|
||||||
DomHandler.focus(this.$refs.firstHiddenFocusableElementOnOverlay);
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
} else {
|
|
||||||
if (this.focusedOptionIndex !== -1) {
|
|
||||||
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.overlayVisible && this.hide(this.filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onBackspaceKey(event, pressedInInputText = false) {
|
|
||||||
if (pressedInInputText) {
|
|
||||||
!this.overlayVisible && this.show();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onOverlayEnter(el) {
|
|
||||||
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
|
||||||
|
|
||||||
DomHandler.addStyles(el, { position: 'absolute', top: '0', left: '0' });
|
|
||||||
this.alignOverlay();
|
|
||||||
this.scrollInView();
|
|
||||||
|
|
||||||
this.autoFilterFocus && DomHandler.focus(this.$refs.filterInput);
|
|
||||||
},
|
|
||||||
onOverlayAfterEnter() {
|
|
||||||
this.bindOutsideClickListener();
|
|
||||||
this.bindScrollListener();
|
|
||||||
this.bindResizeListener();
|
|
||||||
|
|
||||||
this.$emit('show');
|
|
||||||
},
|
|
||||||
onOverlayLeave() {
|
|
||||||
this.unbindOutsideClickListener();
|
|
||||||
this.unbindScrollListener();
|
|
||||||
this.unbindResizeListener();
|
|
||||||
|
|
||||||
this.$emit('hide');
|
|
||||||
this.overlay = null;
|
|
||||||
},
|
|
||||||
onOverlayAfterLeave(el) {
|
|
||||||
ZIndexUtils.clear(el);
|
|
||||||
},
|
|
||||||
alignOverlay() {
|
|
||||||
if (this.appendTo === 'self') {
|
|
||||||
DomHandler.relativePosition(this.overlay, this.$el);
|
|
||||||
} else {
|
|
||||||
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$el) + 'px';
|
|
||||||
DomHandler.absolutePosition(this.overlay, this.$el);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bindOutsideClickListener() {
|
|
||||||
if (!this.outsideClickListener) {
|
|
||||||
this.outsideClickListener = (event) => {
|
|
||||||
if (this.overlayVisible && this.overlay && !this.$el.contains(event.target) && !this.overlay.contains(event.target)) {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('click', this.outsideClickListener);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unbindOutsideClickListener() {
|
|
||||||
if (this.outsideClickListener) {
|
|
||||||
document.removeEventListener('click', this.outsideClickListener);
|
|
||||||
this.outsideClickListener = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bindScrollListener() {
|
|
||||||
if (!this.scrollHandler) {
|
|
||||||
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.container, () => {
|
|
||||||
if (this.overlayVisible) {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scrollHandler.bindScrollListener();
|
|
||||||
},
|
|
||||||
unbindScrollListener() {
|
|
||||||
if (this.scrollHandler) {
|
|
||||||
this.scrollHandler.unbindScrollListener();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bindResizeListener() {
|
|
||||||
if (!this.resizeListener) {
|
|
||||||
this.resizeListener = () => {
|
|
||||||
if (this.overlayVisible && !DomHandler.isTouchDevice()) {
|
|
||||||
this.hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('resize', this.resizeListener);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unbindResizeListener() {
|
|
||||||
if (this.resizeListener) {
|
|
||||||
window.removeEventListener('resize', this.resizeListener);
|
|
||||||
this.resizeListener = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
bindLabelClickListener() {
|
|
||||||
if (!this.editable && !this.labelClickListener) {
|
|
||||||
const label = document.querySelector(`label[for="${this.inputId}"]`);
|
|
||||||
|
|
||||||
if (label && DomHandler.isVisible(label)) {
|
|
||||||
this.labelClickListener = () => {
|
|
||||||
DomHandler.focus(this.$refs.focusInput);
|
|
||||||
};
|
|
||||||
|
|
||||||
label.addEventListener('click', this.labelClickListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unbindLabelClickListener() {
|
|
||||||
if (this.labelClickListener) {
|
|
||||||
const label = document.querySelector(`label[for="${this.inputId}"]`);
|
|
||||||
|
|
||||||
if (label && DomHandler.isVisible(label)) {
|
|
||||||
label.removeEventListener('click', this.labelClickListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasFocusableElements() {
|
|
||||||
return DomHandler.getFocusableElements(this.overlay, ':not([data-p-hidden-focusable="true"])').length > 0;
|
|
||||||
},
|
|
||||||
isOptionMatched(option) {
|
|
||||||
return this.isValidOption(option) && typeof this.getOptionLabel(option) === 'string' && this.getOptionLabel(option)?.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));
|
|
||||||
},
|
|
||||||
isValidOption(option) {
|
|
||||||
return ObjectUtils.isNotEmpty(option) && !(this.isOptionDisabled(option) || this.isOptionGroup(option));
|
|
||||||
},
|
|
||||||
isValidSelectedOption(option) {
|
|
||||||
return this.isValidOption(option) && this.isSelected(option);
|
|
||||||
},
|
|
||||||
isSelected(option) {
|
|
||||||
return this.isValidOption(option) && ObjectUtils.equals(this.modelValue, this.getOptionValue(option), this.equalityKey);
|
|
||||||
},
|
|
||||||
findFirstOptionIndex() {
|
|
||||||
return this.visibleOptions.findIndex((option) => this.isValidOption(option));
|
|
||||||
},
|
|
||||||
findLastOptionIndex() {
|
|
||||||
return ObjectUtils.findLastIndex(this.visibleOptions, (option) => this.isValidOption(option));
|
|
||||||
},
|
|
||||||
findNextOptionIndex(index) {
|
|
||||||
const matchedOptionIndex = index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((option) => this.isValidOption(option)) : -1;
|
|
||||||
|
|
||||||
return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
|
|
||||||
},
|
|
||||||
findPrevOptionIndex(index) {
|
|
||||||
const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidOption(option)) : -1;
|
|
||||||
|
|
||||||
return matchedOptionIndex > -1 ? matchedOptionIndex : index;
|
|
||||||
},
|
|
||||||
findSelectedOptionIndex() {
|
|
||||||
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
|
|
||||||
},
|
|
||||||
findFirstFocusedOptionIndex() {
|
|
||||||
const selectedIndex = this.findSelectedOptionIndex();
|
|
||||||
|
|
||||||
return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
|
|
||||||
},
|
|
||||||
findLastFocusedOptionIndex() {
|
|
||||||
const selectedIndex = this.findSelectedOptionIndex();
|
|
||||||
|
|
||||||
return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
|
|
||||||
},
|
|
||||||
searchOptions(event, char) {
|
|
||||||
this.searchValue = (this.searchValue || '') + char;
|
|
||||||
|
|
||||||
let optionIndex = -1;
|
|
||||||
let matched = false;
|
|
||||||
|
|
||||||
if (ObjectUtils.isNotEmpty(this.searchValue)) {
|
|
||||||
if (this.focusedOptionIndex !== -1) {
|
|
||||||
optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
|
|
||||||
optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
|
|
||||||
} else {
|
|
||||||
optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optionIndex !== -1) {
|
|
||||||
matched = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optionIndex === -1 && this.focusedOptionIndex === -1) {
|
|
||||||
optionIndex = this.findFirstFocusedOptionIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optionIndex !== -1) {
|
|
||||||
this.changeFocusedOptionIndex(event, optionIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.searchTimeout) {
|
|
||||||
clearTimeout(this.searchTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.searchTimeout = setTimeout(() => {
|
|
||||||
this.searchValue = '';
|
|
||||||
this.searchTimeout = null;
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
return matched;
|
|
||||||
},
|
|
||||||
changeFocusedOptionIndex(event, index) {
|
|
||||||
if (this.focusedOptionIndex !== index) {
|
|
||||||
this.focusedOptionIndex = index;
|
|
||||||
this.scrollInView();
|
|
||||||
|
|
||||||
if (this.selectOnFocus) {
|
|
||||||
this.onOptionSelect(event, this.visibleOptions[index], false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scrollInView(index = -1) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
|
|
||||||
const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
|
|
||||||
} else if (!this.virtualScrollerDisabled) {
|
|
||||||
this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autoUpdateModel() {
|
|
||||||
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
|
|
||||||
this.focusedOptionIndex = this.findFirstFocusedOptionIndex();
|
|
||||||
this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex], false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updateModel(event, value) {
|
|
||||||
this.$emit('update:modelValue', value);
|
|
||||||
this.$emit('change', { originalEvent: event, value });
|
|
||||||
},
|
|
||||||
flatOptions(options) {
|
|
||||||
return (options || []).reduce((result, option, index) => {
|
|
||||||
result.push({ optionGroup: option, group: true, index });
|
|
||||||
|
|
||||||
const optionGroupChildren = this.getOptionGroupChildren(option);
|
|
||||||
|
|
||||||
optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}, []);
|
|
||||||
},
|
|
||||||
overlayRef(el) {
|
|
||||||
this.overlay = el;
|
|
||||||
},
|
|
||||||
listRef(el, contentRef) {
|
|
||||||
this.list = el;
|
|
||||||
contentRef && contentRef(el); // For VirtualScroller
|
|
||||||
},
|
|
||||||
virtualScrollerRef(el) {
|
|
||||||
this.virtualScroller = el;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
visibleOptions() {
|
|
||||||
const options = this.optionGroupLabel ? this.flatOptions(this.options) : this.options || [];
|
|
||||||
|
|
||||||
if (this.filterValue) {
|
|
||||||
const filteredOptions = FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
|
||||||
|
|
||||||
if (this.optionGroupLabel) {
|
|
||||||
const optionGroups = this.options || [];
|
|
||||||
const filtered = [];
|
|
||||||
|
|
||||||
optionGroups.forEach((group) => {
|
|
||||||
const groupChildren = this.getOptionGroupChildren(group);
|
|
||||||
const filteredItems = groupChildren.filter((item) => filteredOptions.includes(item));
|
|
||||||
|
|
||||||
if (filteredItems.length > 0) filtered.push({ ...group, [typeof this.optionGroupChildren === 'string' ? this.optionGroupChildren : 'items']: [...filteredItems] });
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.flatOptions(filtered);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filteredOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
hasSelectedOption() {
|
|
||||||
return ObjectUtils.isNotEmpty(this.modelValue);
|
|
||||||
},
|
|
||||||
label() {
|
|
||||||
const selectedOptionIndex = this.findSelectedOptionIndex();
|
|
||||||
|
|
||||||
return selectedOptionIndex !== -1 ? this.getOptionLabel(this.visibleOptions[selectedOptionIndex]) : this.placeholder || 'p-emptylabel';
|
|
||||||
},
|
|
||||||
editableInputValue() {
|
|
||||||
const selectedOptionIndex = this.findSelectedOptionIndex();
|
|
||||||
|
|
||||||
return selectedOptionIndex !== -1 ? this.getOptionLabel(this.visibleOptions[selectedOptionIndex]) : this.modelValue || '';
|
|
||||||
},
|
|
||||||
equalityKey() {
|
|
||||||
return this.optionValue ? null : this.dataKey;
|
|
||||||
},
|
|
||||||
searchFields() {
|
|
||||||
return this.filterFields || [this.optionLabel];
|
|
||||||
},
|
|
||||||
filterResultMessageText() {
|
|
||||||
return ObjectUtils.isNotEmpty(this.visibleOptions) ? this.filterMessageText.replaceAll('{0}', this.visibleOptions.length) : this.emptyFilterMessageText;
|
|
||||||
},
|
|
||||||
filterMessageText() {
|
|
||||||
return this.filterMessage || this.$primevue.config.locale.searchMessage || '';
|
|
||||||
},
|
|
||||||
emptyFilterMessageText() {
|
|
||||||
return this.emptyFilterMessage || this.$primevue.config.locale.emptySearchMessage || this.$primevue.config.locale.emptyFilterMessage || '';
|
|
||||||
},
|
|
||||||
emptyMessageText() {
|
|
||||||
return this.emptyMessage || this.$primevue.config.locale.emptyMessage || '';
|
|
||||||
},
|
|
||||||
selectionMessageText() {
|
|
||||||
return this.selectionMessage || this.$primevue.config.locale.selectionMessage || '';
|
|
||||||
},
|
|
||||||
emptySelectionMessageText() {
|
|
||||||
return this.emptySelectionMessage || this.$primevue.config.locale.emptySelectionMessage || '';
|
|
||||||
},
|
|
||||||
selectedMessageText() {
|
|
||||||
return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', '1') : this.emptySelectionMessageText;
|
|
||||||
},
|
|
||||||
focusedOptionId() {
|
|
||||||
return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null;
|
|
||||||
},
|
|
||||||
ariaSetSize() {
|
|
||||||
return this.visibleOptions.filter((option) => !this.isOptionGroup(option)).length;
|
|
||||||
},
|
|
||||||
virtualScrollerDisabled() {
|
|
||||||
return !this.virtualScrollerOptions;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
directives: {
|
|
||||||
ripple: Ripple
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
VirtualScroller,
|
|
||||||
Portal,
|
|
||||||
TimesIcon,
|
|
||||||
ChevronDownIcon,
|
|
||||||
SpinnerIcon,
|
|
||||||
SearchIcon,
|
|
||||||
CheckIcon,
|
|
||||||
BlankIcon
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { BaseStyle } from '../../base/style';
|
import { SelectStyle } from '../../select/style/SelectStyle';
|
||||||
|
|
||||||
export interface DropdownStyle extends BaseStyle {}
|
export interface DropdownStyle extends SelectStyle {}
|
||||||
|
|
|
@ -1,58 +1,5 @@
|
||||||
import BaseStyle from 'primevue/base/style';
|
import BaseStyle from 'primevue/base/style';
|
||||||
|
|
||||||
const classes = {
|
|
||||||
root: ({ instance, props, state }) => [
|
|
||||||
'p-select p-component p-inputwrapper',
|
|
||||||
{
|
|
||||||
'p-disabled': props.disabled,
|
|
||||||
'p-invalid': props.invalid,
|
|
||||||
'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled',
|
|
||||||
'p-focus': state.focused,
|
|
||||||
'p-inputwrapper-filled': instance.hasSelectedOption,
|
|
||||||
'p-inputwrapper-focus': state.focused || state.overlayVisible,
|
|
||||||
'p-select-open': state.overlayVisible
|
|
||||||
}
|
|
||||||
],
|
|
||||||
input: ({ instance, props }) => [
|
|
||||||
'p-select-label',
|
|
||||||
{
|
|
||||||
'p-placeholder': !props.editable && instance.label === props.placeholder,
|
|
||||||
'p-select-label-empty': !props.editable && !instance.$slots['value'] && (instance.label === 'p-emptylabel' || instance.label.length === 0)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
clearIcon: 'p-select-clear-icon',
|
|
||||||
trigger: 'p-select-dropdown',
|
|
||||||
loadingicon: 'p-select-loading-icon',
|
|
||||||
dropdownIcon: 'p-select-dropdown-icon',
|
|
||||||
panel: ({ instance }) => [
|
|
||||||
'p-select-overlay p-component',
|
|
||||||
{
|
|
||||||
'p-ripple-disabled': instance.$primevue.config.ripple === false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
header: 'p-select-header',
|
|
||||||
filterContainer: 'p-select-filter-container',
|
|
||||||
filterInput: 'p-select-filter',
|
|
||||||
filterIcon: 'p-select-filter-icon',
|
|
||||||
wrapper: 'p-select-list-container',
|
|
||||||
list: 'p-select-list',
|
|
||||||
itemGroup: 'p-select-option-group',
|
|
||||||
itemGroupLabel: 'p-select-option-group-label',
|
|
||||||
item: ({ instance, props, state, option, focusedOption }) => [
|
|
||||||
'p-select-option',
|
|
||||||
{
|
|
||||||
'p-select-option-selected': instance.isSelected(option) && props.highlightOnSelect,
|
|
||||||
'p-focus': state.focusedOptionIndex === focusedOption,
|
|
||||||
'p-disabled': instance.isOptionDisabled(option)
|
|
||||||
}
|
|
||||||
],
|
|
||||||
itemLabel: 'p-select-option-label',
|
|
||||||
checkIcon: 'p-select-option-check-icon',
|
|
||||||
blankIcon: 'p-select-option-blank-icon',
|
|
||||||
emptyMessage: 'p-select-empty-message'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default BaseStyle.extend({
|
export default BaseStyle.extend({
|
||||||
name: 'dropdown',
|
name: 'dropdown'
|
||||||
classes
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import BaseComponent from 'primevue/basecomponent';
|
import BaseComponent from 'primevue/basecomponent';
|
||||||
import DropdownStyle from 'primevue/dropdown/style';
|
import SelectStyle from 'primevue/select/style';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BaseDropdown',
|
name: 'BaseSelect',
|
||||||
extends: BaseComponent,
|
extends: BaseComponent,
|
||||||
props: {
|
props: {
|
||||||
modelValue: null,
|
modelValue: null,
|
||||||
|
@ -163,7 +163,7 @@ export default {
|
||||||
default: null
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: DropdownStyle,
|
style: SelectStyle,
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
$parentInstance: this
|
$parentInstance: this
|
|
@ -0,0 +1,762 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Select also known as Select, is used to choose an item from a collection of options.
|
||||||
|
*
|
||||||
|
* [Live Demo](https://www.primevue.org/select/)
|
||||||
|
*
|
||||||
|
* @module select
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { TransitionProps, VNode } from 'vue';
|
||||||
|
import { ComponentHooks } from '../basecomponent';
|
||||||
|
import { PassThroughOptions } from '../passthrough';
|
||||||
|
import { ClassComponent, DesignToken, GlobalComponentConstructor, HintedString, PassThrough } from '../ts-helpers';
|
||||||
|
import { VirtualScrollerItemOptions, VirtualScrollerPassThroughOptionType, VirtualScrollerProps } from '../virtualscroller';
|
||||||
|
|
||||||
|
export declare type SelectPassThroughOptionType<T = any> = SelectPassThroughAttributes | ((options: SelectPassThroughMethodOptions<T>) => SelectPassThroughAttributes | string) | string | null | undefined;
|
||||||
|
|
||||||
|
export declare type SelectPassThroughTransitionType<T = any> = TransitionProps | ((options: SelectPassThroughMethodOptions<T>) => TransitionProps) | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom passthrough(pt) option method.
|
||||||
|
*/
|
||||||
|
export interface SelectPassThroughMethodOptions<T> {
|
||||||
|
/**
|
||||||
|
* Defines instance.
|
||||||
|
*/
|
||||||
|
instance: any;
|
||||||
|
/**
|
||||||
|
* Defines valid properties.
|
||||||
|
*/
|
||||||
|
props: SelectProps;
|
||||||
|
/**
|
||||||
|
* Defines current inline state.
|
||||||
|
*/
|
||||||
|
state: SelectState;
|
||||||
|
/**
|
||||||
|
* Defines parent instance.
|
||||||
|
*/
|
||||||
|
parent: T | any;
|
||||||
|
/**
|
||||||
|
* Defines current options.
|
||||||
|
*/
|
||||||
|
context: SelectContext;
|
||||||
|
/**
|
||||||
|
* Defines passthrough(pt) options in global config.
|
||||||
|
*/
|
||||||
|
global: object | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom change event.
|
||||||
|
* @see {@link SelectEmits.change}
|
||||||
|
*/
|
||||||
|
export interface SelectChangeEvent {
|
||||||
|
/**
|
||||||
|
* Browser event.
|
||||||
|
*/
|
||||||
|
originalEvent: Event;
|
||||||
|
/**
|
||||||
|
* Selected option value
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom filter event.
|
||||||
|
* @see {@link SelectEmits.filter}
|
||||||
|
*/
|
||||||
|
export interface SelectFilterEvent {
|
||||||
|
/**
|
||||||
|
* Browser event.
|
||||||
|
*/
|
||||||
|
originalEvent: Event;
|
||||||
|
/**
|
||||||
|
* Filter value
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom passthrough(pt) options.
|
||||||
|
* @see {@link SelectProps.pt}
|
||||||
|
*/
|
||||||
|
export interface SelectPassThroughOptions<T = any> {
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the root's DOM element.
|
||||||
|
*/
|
||||||
|
root?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the input's DOM element.
|
||||||
|
*/
|
||||||
|
input?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the clear icon's DOM element.
|
||||||
|
*/
|
||||||
|
clearIcon?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the trigger' DOM element.
|
||||||
|
*/
|
||||||
|
trigger?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the loading icon's DOM element.
|
||||||
|
*/
|
||||||
|
loadingIcon?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the panel's DOM element.
|
||||||
|
*/
|
||||||
|
panel?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the header's DOM element.
|
||||||
|
*/
|
||||||
|
header?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the filter container's DOM element.
|
||||||
|
*/
|
||||||
|
filterContainer?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the filter input's DOM element.
|
||||||
|
*/
|
||||||
|
filterInput?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the filter icon's DOM element.
|
||||||
|
*/
|
||||||
|
filterIcon?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the wrapper's DOM element.
|
||||||
|
*/
|
||||||
|
wrapper?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the VirtualScroller component.
|
||||||
|
* @see {@link VirtualScrollerPassThroughOptionType}
|
||||||
|
*/
|
||||||
|
virtualScroller?: VirtualScrollerPassThroughOptionType;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the list's DOM element.
|
||||||
|
*/
|
||||||
|
list?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the item group's DOM element.
|
||||||
|
*/
|
||||||
|
itemGroup?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the item group label's DOM element.
|
||||||
|
*/
|
||||||
|
itemGroupLabel?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the item's DOM element.
|
||||||
|
*/
|
||||||
|
item?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the item label's DOM element.
|
||||||
|
*/
|
||||||
|
itemLabel?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the check icon's DOM element.
|
||||||
|
*/
|
||||||
|
checkIcon?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the bank icon's DOM element.
|
||||||
|
*/
|
||||||
|
blankIcon?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the empty message's DOM element.
|
||||||
|
*/
|
||||||
|
emptyMessage?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the hidden first focusable element's DOM element.
|
||||||
|
*/
|
||||||
|
hiddenFirstFocusableEl?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the hidden filter result's DOM element.
|
||||||
|
*/
|
||||||
|
hiddenFilterResult?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the hidden empty message's DOM element.
|
||||||
|
*/
|
||||||
|
hiddenEmptyMessage?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the hidden selected message's DOM element.
|
||||||
|
*/
|
||||||
|
hiddenSelectedMessage?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to pass attributes to the hidden last focusable element's DOM element.
|
||||||
|
*/
|
||||||
|
hiddenLastFocusableEl?: SelectPassThroughOptionType<T>;
|
||||||
|
/**
|
||||||
|
* Used to manage all lifecycle hooks.
|
||||||
|
* @see {@link BaseComponent.ComponentHooks}
|
||||||
|
*/
|
||||||
|
hooks?: ComponentHooks;
|
||||||
|
/**
|
||||||
|
* Used to control Vue Transition API.
|
||||||
|
*/
|
||||||
|
transition?: SelectPassThroughTransitionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom passthrough attributes for each DOM elements
|
||||||
|
*/
|
||||||
|
export interface SelectPassThroughAttributes {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines current inline state in Select component.
|
||||||
|
*/
|
||||||
|
export interface SelectState {
|
||||||
|
/**
|
||||||
|
* Current id state as a string.
|
||||||
|
*/
|
||||||
|
id: string;
|
||||||
|
/**
|
||||||
|
* Current focused state as a boolean.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
focused: boolean;
|
||||||
|
/**
|
||||||
|
* Current focused item index as a number.
|
||||||
|
* @defaultValue -1
|
||||||
|
*/
|
||||||
|
focusedOptionIndex: number;
|
||||||
|
/**
|
||||||
|
* Current filter value state as a string.
|
||||||
|
*/
|
||||||
|
filterValue: string;
|
||||||
|
/**
|
||||||
|
* Current overlay visible state as a boolean.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
overlayVisible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines current options in Select component.
|
||||||
|
*/
|
||||||
|
export interface SelectContext {
|
||||||
|
/**
|
||||||
|
* Current item option.
|
||||||
|
*/
|
||||||
|
option: any;
|
||||||
|
/**
|
||||||
|
* Current item index.
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
/**
|
||||||
|
* Current selection state of the item as a boolean.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
selected: boolean;
|
||||||
|
/**
|
||||||
|
* Current focus state of the item as a boolean.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
focused: boolean;
|
||||||
|
/**
|
||||||
|
* Current disabled state of the item as a boolean.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
disabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines valid properties in Select component.
|
||||||
|
*/
|
||||||
|
export interface SelectProps {
|
||||||
|
/**
|
||||||
|
* Value of the component.
|
||||||
|
*/
|
||||||
|
modelValue?: any;
|
||||||
|
/**
|
||||||
|
* An array of select items to display as the available options.
|
||||||
|
*/
|
||||||
|
options?: any[];
|
||||||
|
/**
|
||||||
|
* Property name or getter function to use as the label of an option.
|
||||||
|
*/
|
||||||
|
optionLabel?: string | ((data: any) => string) | undefined;
|
||||||
|
/**
|
||||||
|
* Property name or getter function to use as the value of an option, defaults to the option itself when not defined.
|
||||||
|
*/
|
||||||
|
optionValue?: string | ((data: any) => any) | undefined;
|
||||||
|
/**
|
||||||
|
* Property name or getter function to use as the disabled flag of an option, defaults to false when not defined.
|
||||||
|
*/
|
||||||
|
optionDisabled?: string | ((data: any) => boolean) | undefined;
|
||||||
|
/**
|
||||||
|
* Property name or getter function to use as the label of an option group.
|
||||||
|
*/
|
||||||
|
optionGroupLabel?: string | ((data: any) => string) | undefined;
|
||||||
|
/**
|
||||||
|
* Property name or getter function that refers to the children options of option group.
|
||||||
|
*/
|
||||||
|
optionGroupChildren?: string | ((data: any) => any[]) | undefined;
|
||||||
|
/**
|
||||||
|
* Height of the viewport, a scrollbar is defined if height of list exceeds this value.
|
||||||
|
* @defaultValue 14rem
|
||||||
|
*/
|
||||||
|
scrollHeight?: string | undefined;
|
||||||
|
/**
|
||||||
|
* When specified, displays a filter input at header.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
filter?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Placeholder text to show when filter input is empty.
|
||||||
|
*/
|
||||||
|
filterPlaceholder?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Locale to use in filtering. The default locale is the host environment's current locale.
|
||||||
|
*/
|
||||||
|
filterLocale?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Defines the filtering algorithm to use when searching the options.
|
||||||
|
* @defaultValue contains
|
||||||
|
*/
|
||||||
|
filterMatchMode?: HintedString<'contains' | 'startsWith' | 'endsWith'> | undefined;
|
||||||
|
/**
|
||||||
|
* Fields used when filtering the options, defaults to optionLabel.
|
||||||
|
*/
|
||||||
|
filterFields?: string[] | undefined;
|
||||||
|
/**
|
||||||
|
* When present, custom value instead of predefined options can be entered using the editable input field.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
editable?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Default text to display when no option is selected.
|
||||||
|
*/
|
||||||
|
placeholder?: 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 component should be disabled.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
disabled?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Specifies the input variant of the component.
|
||||||
|
* @defaultValue outlined
|
||||||
|
*/
|
||||||
|
variant?: 'outlined' | 'filled' | undefined;
|
||||||
|
/**
|
||||||
|
* A property to uniquely identify an option.
|
||||||
|
*/
|
||||||
|
dataKey?: string | undefined;
|
||||||
|
/**
|
||||||
|
* When enabled, a clear icon is displayed to clear the value.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
showClear?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Identifier of the underlying input element.
|
||||||
|
*/
|
||||||
|
inputId?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Inline style of the input field.
|
||||||
|
*/
|
||||||
|
inputStyle?: object | undefined;
|
||||||
|
/**
|
||||||
|
* Style class of the input field.
|
||||||
|
*/
|
||||||
|
inputClass?: string | object | undefined;
|
||||||
|
/**
|
||||||
|
* Inline style of the overlay panel.
|
||||||
|
*/
|
||||||
|
panelStyle?: object | undefined;
|
||||||
|
/**
|
||||||
|
* Style class of the overlay panel.
|
||||||
|
*/
|
||||||
|
panelClass?: string | object | undefined;
|
||||||
|
/**
|
||||||
|
* A valid query selector or an HTMLElement to specify where the overlay gets attached.
|
||||||
|
* @defaultValue body
|
||||||
|
*/
|
||||||
|
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
||||||
|
/**
|
||||||
|
* Whether the select is in loading state.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
loading?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in clear button.
|
||||||
|
* @deprecated since v3.27.0. Use 'clearicon' slot.
|
||||||
|
*/
|
||||||
|
clearIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in the select.
|
||||||
|
* @deprecated since v3.27.0. Use 'dropdownicon' slot.
|
||||||
|
*/
|
||||||
|
dropdownIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in filter input.
|
||||||
|
* @deprecated since v3.27.0. Use 'filtericon' slot.
|
||||||
|
*/
|
||||||
|
filterIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Icon to display in loading state.
|
||||||
|
* @deprecated since v3.27.0. Use 'loadingicon' slot.
|
||||||
|
*/
|
||||||
|
loadingIcon?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Clears the filter value when hiding the select.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
resetFilterOnHide?: boolean;
|
||||||
|
/**
|
||||||
|
* Clears the filter value when clicking on the clear icon.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
resetFilterOnClear?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it.
|
||||||
|
*/
|
||||||
|
virtualScrollerOptions?: VirtualScrollerProps;
|
||||||
|
/**
|
||||||
|
* Whether to focus on the first visible or selected element when the overlay panel is shown.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
autoOptionFocus?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Whether to focus on the filter element when the overlay panel is shown.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
autoFilterFocus?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* When enabled, the focused option is selected.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
selectOnFocus?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* When enabled, the focus is placed on the hovered option.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
focusOnHover?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Whether the selected option will be add highlight class.
|
||||||
|
* @defaultValue true
|
||||||
|
*/
|
||||||
|
highlightOnSelect?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Whether the selected option will be shown with a check mark.
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
checkmark?: boolean | undefined;
|
||||||
|
/**
|
||||||
|
* Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
|
||||||
|
* @defaultValue '{0} results are available'
|
||||||
|
*/
|
||||||
|
filterMessage?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Text to be displayed in hidden accessible field when options are selected. Defaults to value from PrimeVue locale configuration.
|
||||||
|
* @defaultValue '{0} items selected'
|
||||||
|
*/
|
||||||
|
selectionMessage?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Text to be displayed in hidden accessible field when any option is not selected. Defaults to value from PrimeVue locale configuration.
|
||||||
|
* @defaultValue No selected item
|
||||||
|
*/
|
||||||
|
emptySelectionMessage?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Text to display when filtering does not return any results. Defaults to value from PrimeVue locale configuration.
|
||||||
|
* @defaultValue No results found
|
||||||
|
*/
|
||||||
|
emptyFilterMessage?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Text to display when there are no options available. Defaults to value from PrimeVue locale configuration.
|
||||||
|
* @defaultValue No results found
|
||||||
|
*/
|
||||||
|
emptyMessage?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Index of the element in tabbing order.
|
||||||
|
*/
|
||||||
|
tabindex?: number | string | undefined;
|
||||||
|
/**
|
||||||
|
* Defines a string value that labels an interactive element.
|
||||||
|
*/
|
||||||
|
ariaLabel?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Identifier of the underlying input element.
|
||||||
|
*/
|
||||||
|
ariaLabelledby?: 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 {SelectPassThroughOptions}
|
||||||
|
*/
|
||||||
|
pt?: PassThrough<SelectPassThroughOptions>;
|
||||||
|
/**
|
||||||
|
* 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 Select component.
|
||||||
|
*/
|
||||||
|
export interface SelectSlots {
|
||||||
|
/**
|
||||||
|
* Custom value template.
|
||||||
|
* @param {Object} scope - value slot's params.
|
||||||
|
*/
|
||||||
|
value(scope: {
|
||||||
|
/**
|
||||||
|
* Value of the component
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
/**
|
||||||
|
* Placeholder prop value
|
||||||
|
*/
|
||||||
|
placeholder: string;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom indicator template.
|
||||||
|
* @deprecated since v3.27.0. Use 'dropdownicon or loadingicon' slots.
|
||||||
|
*/
|
||||||
|
indicator(): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom header template of panel.
|
||||||
|
* @param {Object} scope - header slot's params.
|
||||||
|
*/
|
||||||
|
header(scope: {
|
||||||
|
/**
|
||||||
|
* Value of the component
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
/**
|
||||||
|
* Displayed options
|
||||||
|
*/
|
||||||
|
options: any[];
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom footer template of panel.
|
||||||
|
* @param {Object} scope - footer slot's params.
|
||||||
|
*/
|
||||||
|
footer(scope: {
|
||||||
|
/**
|
||||||
|
* Value of the component
|
||||||
|
*/
|
||||||
|
value: any;
|
||||||
|
/**
|
||||||
|
* Displayed options
|
||||||
|
*/
|
||||||
|
options: any[];
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom option template.
|
||||||
|
* @param {Object} scope - option slot's params.
|
||||||
|
*/
|
||||||
|
option(scope: {
|
||||||
|
/**
|
||||||
|
* Option instance
|
||||||
|
*/
|
||||||
|
option: any;
|
||||||
|
/**
|
||||||
|
* Index of the option
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom option group template.
|
||||||
|
* @param {Object} scope - option group slot's params.
|
||||||
|
*/
|
||||||
|
optiongroup(scope: {
|
||||||
|
/**
|
||||||
|
* Option instance
|
||||||
|
*/
|
||||||
|
option: any;
|
||||||
|
/**
|
||||||
|
* Index of the option
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom empty filter template.
|
||||||
|
*/
|
||||||
|
emptyfilter(): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom empty template.
|
||||||
|
*/
|
||||||
|
empty(): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom content template.
|
||||||
|
* @param {Object} scope - content slot's params.
|
||||||
|
*/
|
||||||
|
content(scope: {
|
||||||
|
/**
|
||||||
|
* An array of objects to display for virtualscroller
|
||||||
|
*/
|
||||||
|
items: any;
|
||||||
|
/**
|
||||||
|
* Style class of the component
|
||||||
|
*/
|
||||||
|
styleClass: string;
|
||||||
|
/**
|
||||||
|
* Referance of the content
|
||||||
|
* @param {HTMLElement} el - Element of 'ref' property
|
||||||
|
*/
|
||||||
|
contentRef: (el: any) => void;
|
||||||
|
/**
|
||||||
|
* Options of the items
|
||||||
|
* @param {number} index - Rendered index
|
||||||
|
* @return {@link VirtualScrollerItemOptions}
|
||||||
|
*/
|
||||||
|
getItemOptions: (index: number) => VirtualScrollerItemOptions;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom loader template.
|
||||||
|
* @param {Object} scope - loader slot's params.
|
||||||
|
*/
|
||||||
|
loader(scope: {
|
||||||
|
/**
|
||||||
|
* Options of the loader items for virtualscroller
|
||||||
|
*/
|
||||||
|
options: any[];
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom clear icon template.
|
||||||
|
* @param {Object} scope - clear icon slot's params.
|
||||||
|
*/
|
||||||
|
clearicon(scope: {
|
||||||
|
/**
|
||||||
|
* Style class of the clear icon
|
||||||
|
*/
|
||||||
|
class: any;
|
||||||
|
/**
|
||||||
|
* Clear icon click function.
|
||||||
|
* @param {Event} event - Browser event
|
||||||
|
* @deprecated since v3.39.0. Use 'clearCallback' property instead.
|
||||||
|
*/
|
||||||
|
onClick: (event: Event) => void;
|
||||||
|
/**
|
||||||
|
* Clear icon click function.
|
||||||
|
* @param {Event} event - Browser event
|
||||||
|
*/
|
||||||
|
clearCallback: (event: Event) => void;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom select icon template.
|
||||||
|
* @param {Object} scope - select icon slot's params.
|
||||||
|
*/
|
||||||
|
dropdownicon(scope: {
|
||||||
|
/**
|
||||||
|
* Style class of the select icon
|
||||||
|
*/
|
||||||
|
class: any;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom loading icon template.
|
||||||
|
* @param {Object} scope - loading icon slot's params.
|
||||||
|
*/
|
||||||
|
loadingicon(scope: {
|
||||||
|
/**
|
||||||
|
* Style class of the loading icon
|
||||||
|
*/
|
||||||
|
class: any;
|
||||||
|
}): VNode[];
|
||||||
|
/**
|
||||||
|
* Custom filter icon template.
|
||||||
|
* @param {Object} scope - filter icon slot's params.
|
||||||
|
*/
|
||||||
|
filtericon(scope: {
|
||||||
|
/**
|
||||||
|
* Style class of the filter icon
|
||||||
|
*/
|
||||||
|
class: any;
|
||||||
|
}): VNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines valid emits in Select component.
|
||||||
|
*/
|
||||||
|
export interface SelectEmits {
|
||||||
|
/**
|
||||||
|
* Emitted when the value changes.
|
||||||
|
* @param {*} value - New value.
|
||||||
|
*/
|
||||||
|
'update:modelValue'(value: any): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke on value change.
|
||||||
|
* @param {SelectChangeEvent} event - Custom change event.
|
||||||
|
*/
|
||||||
|
change(event: SelectChangeEvent): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke when the component receives focus.
|
||||||
|
* @param {Event} event - Browser event.
|
||||||
|
*/
|
||||||
|
focus(event: Event): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke when the component loses focus.
|
||||||
|
* @param {Event} event - Browser event.
|
||||||
|
*/
|
||||||
|
blur(event: Event): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke before the overlay is shown.
|
||||||
|
*/
|
||||||
|
'before-show'(): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke before the overlay is hidden.
|
||||||
|
*/
|
||||||
|
'before-hide'(): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke when the overlay is shown.
|
||||||
|
*/
|
||||||
|
show(): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke when the overlay is hidden.
|
||||||
|
*/
|
||||||
|
hide(): void;
|
||||||
|
/**
|
||||||
|
* Callback to invoke on filter input.
|
||||||
|
* @param {SelectFilterEvent} event - Custom filter event.
|
||||||
|
*/
|
||||||
|
filter(event: SelectFilterEvent): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **PrimeVue - Select**
|
||||||
|
*
|
||||||
|
* _Select is used to choose an item from a collection of options._
|
||||||
|
*
|
||||||
|
* [Live Demo](https://www.primevue.org/select/)
|
||||||
|
* --- ---
|
||||||
|
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
|
||||||
|
*
|
||||||
|
* @group Component
|
||||||
|
*/
|
||||||
|
declare class Select extends ClassComponent<SelectProps, SelectSlots, SelectEmits> {
|
||||||
|
/**
|
||||||
|
* Shows the overlay.
|
||||||
|
* @param {boolean} [isFocus] - Decides whether to focus on the component. @defaultValue false.
|
||||||
|
*
|
||||||
|
* @memberof Select
|
||||||
|
*/
|
||||||
|
show: (isFocus?: boolean) => void;
|
||||||
|
/**
|
||||||
|
* Hides the overlay.
|
||||||
|
* @param {boolean} [isFocus] - Decides whether to focus on the component. @defaultValue false.
|
||||||
|
*
|
||||||
|
* @memberof Select
|
||||||
|
*/
|
||||||
|
hide: (isFocus?: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
Select: GlobalComponentConstructor<Select>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Select;
|
|
@ -1,13 +1,13 @@
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import PrimeVue from 'primevue/config';
|
import PrimeVue from 'primevue/config';
|
||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
import Dropdown from './Dropdown.vue';
|
import Select from './Select.vue';
|
||||||
|
|
||||||
describe('Dropdown.vue', () => {
|
describe('Select.vue', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -19,10 +19,10 @@ describe('Dropdown.vue', () => {
|
||||||
await wrapper.trigger('click');
|
await wrapper.trigger('click');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should Dropdown exist', () => {
|
it('should Select exist', () => {
|
||||||
expect(wrapper.find('.p-dropdown.p-component').exists()).toBe(true);
|
expect(wrapper.find('.p-select.p-component').exists()).toBe(true);
|
||||||
expect(wrapper.find('.p-dropdown-panel').exists()).toBe(true);
|
expect(wrapper.find('.p-select-panel').exists()).toBe(true);
|
||||||
expect(wrapper.find('.p-dropdown-empty-message').exists()).toBe(true);
|
expect(wrapper.find('.p-select-empty-message').exists()).toBe(true);
|
||||||
expect(wrapper.find('.p-inputwrapper-filled').exists()).toBe(false);
|
expect(wrapper.find('.p-inputwrapper-filled').exists()).toBe(false);
|
||||||
expect(wrapper.find('.p-inputwrapper-focus').exists()).toBe(true);
|
expect(wrapper.find('.p-inputwrapper-focus').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@ describe('option checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -57,11 +57,11 @@ describe('option checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the options', () => {
|
it('should show the options', () => {
|
||||||
expect(wrapper.find('.p-dropdown-label.p-placeholder').text()).toBe('Select a City');
|
expect(wrapper.find('.p-select-label.p-placeholder').text()).toBe('Select a City');
|
||||||
expect(wrapper.find('.p-dropdown-items-wrapper > .p-dropdown-items').exists()).toBe(true);
|
expect(wrapper.find('.p-select-items-wrapper > .p-select-items').exists()).toBe(true);
|
||||||
expect(wrapper.find('.p-dropdown-item').exists()).toBe(true);
|
expect(wrapper.find('.p-select-item').exists()).toBe(true);
|
||||||
expect(wrapper.findAll('.p-dropdown-item').length).toBe(5);
|
expect(wrapper.findAll('.p-select-item').length).toBe(5);
|
||||||
expect(wrapper.findAll('.p-dropdown-item')[0].text()).toBe('New York');
|
expect(wrapper.findAll('.p-select-item')[0].text()).toBe('New York');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ describe('clear checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -87,13 +87,13 @@ describe('clear checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have correct icon', () => {
|
it('should have correct icon', () => {
|
||||||
expect(wrapper.find('.p-dropdown-clear-icon').classes()).toContain('pi-discord');
|
expect(wrapper.find('.p-select-clear-icon').classes()).toContain('pi-discord');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear with delete key', async () => {
|
it('should clear with delete key', async () => {
|
||||||
const updateModelSpy = vi.spyOn(wrapper.vm, 'updateModel');
|
const updateModelSpy = vi.spyOn(wrapper.vm, 'updateModel');
|
||||||
|
|
||||||
await wrapper.find('.p-dropdown-label.p-inputtext').trigger('keydown', { code: 'Delete' });
|
await wrapper.find('.p-select-label.p-inputtext').trigger('keydown', { code: 'Delete' });
|
||||||
expect(updateModelSpy).toHaveBeenCalledOnce();
|
expect(updateModelSpy).toHaveBeenCalledOnce();
|
||||||
expect(updateModelSpy).toHaveBeenCalledWith(expect.any(KeyboardEvent), null);
|
expect(updateModelSpy).toHaveBeenCalledWith(expect.any(KeyboardEvent), null);
|
||||||
});
|
});
|
||||||
|
@ -103,7 +103,7 @@ describe('editable checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -129,8 +129,8 @@ describe('editable checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the options', () => {
|
it('should show the options', () => {
|
||||||
expect(wrapper.find('.p-dropdown-label.p-placeholder').exists()).toBe(false);
|
expect(wrapper.find('.p-select-label.p-placeholder').exists()).toBe(false);
|
||||||
expect(wrapper.find('.p-dropdown-label.p-inputtext').exists()).toBe(true);
|
expect(wrapper.find('.p-select-label.p-inputtext').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ describe('option groups checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -188,8 +188,8 @@ describe('option groups checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the option groups', () => {
|
it('should show the option groups', () => {
|
||||||
expect(wrapper.findAll('.p-dropdown-item-group').length).toBe(3);
|
expect(wrapper.findAll('.p-select-item-group').length).toBe(3);
|
||||||
expect(wrapper.findAll('.p-dropdown-item-group')[0].text()).toBe('Germany');
|
expect(wrapper.findAll('.p-select-item-group')[0].text()).toBe('Germany');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ describe('templating checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -249,7 +249,7 @@ describe('empty templating checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -270,8 +270,8 @@ describe('empty templating checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should see empty slots', () => {
|
it('should see empty slots', () => {
|
||||||
expect(wrapper.find('.p-dropdown-empty-message').exists()).toBe(true);
|
expect(wrapper.find('.p-select-empty-message').exists()).toBe(true);
|
||||||
expect(wrapper.find('.p-dropdown-empty-message').text()).toBe('Need options prop');
|
expect(wrapper.find('.p-select-empty-message').text()).toBe('Need options prop');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ describe('loader checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -306,11 +306,11 @@ describe('loader checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the loader', async () => {
|
it('should show the loader', async () => {
|
||||||
expect(wrapper.find('.p-dropdown-trigger-icon').classes()).toContain('pi-discord');
|
expect(wrapper.find('.p-select-trigger-icon').classes()).toContain('pi-discord');
|
||||||
|
|
||||||
await wrapper.setProps({ loading: false });
|
await wrapper.setProps({ loading: false });
|
||||||
|
|
||||||
expect(wrapper.find('.p-dropdown-trigger-icon').classes()).not.toContain('pi-discord');
|
expect(wrapper.find('.p-select-trigger-icon').classes()).not.toContain('pi-discord');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ describe('filter checks', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
wrapper = mount(Dropdown, {
|
wrapper = mount(Select, {
|
||||||
global: {
|
global: {
|
||||||
plugins: [PrimeVue],
|
plugins: [PrimeVue],
|
||||||
stubs: {
|
stubs: {
|
||||||
|
@ -348,8 +348,8 @@ describe('filter checks', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make filtering', async () => {
|
it('should make filtering', async () => {
|
||||||
const filterInput = wrapper.find('.p-dropdown-filter');
|
const filterInput = wrapper.find('.p-select-filter');
|
||||||
const filterIcon = wrapper.find('.p-dropdown-filter-icon');
|
const filterIcon = wrapper.find('.p-select-filter-icon');
|
||||||
|
|
||||||
expect(filterInput.exists()).toBe(true);
|
expect(filterInput.exists()).toBe(true);
|
||||||
expect(filterIcon.classes()).toContain('pi-discord');
|
expect(filterIcon.classes()).toContain('pi-discord');
|
||||||
|
@ -364,6 +364,6 @@ describe('filter checks', () => {
|
||||||
|
|
||||||
await wrapper.setData({ filterValue: 'c' });
|
await wrapper.setData({ filterValue: 'c' });
|
||||||
|
|
||||||
expect(wrapper.findAll('.p-dropdown-item').length).toBe(2);
|
expect(wrapper.findAll('.p-select-item').length).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -0,0 +1,991 @@
|
||||||
|
<template>
|
||||||
|
<div ref="container" :id="id" :class="cx('root')" @click="onContainerClick" v-bind="ptmi('root')">
|
||||||
|
<input
|
||||||
|
v-if="editable"
|
||||||
|
ref="focusInput"
|
||||||
|
:id="inputId"
|
||||||
|
type="text"
|
||||||
|
:class="[cx('input'), inputClass]"
|
||||||
|
:style="inputStyle"
|
||||||
|
:value="editableInputValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:tabindex="!disabled ? tabindex : -1"
|
||||||
|
:disabled="disabled"
|
||||||
|
autocomplete="off"
|
||||||
|
role="combobox"
|
||||||
|
:aria-label="ariaLabel"
|
||||||
|
:aria-labelledby="ariaLabelledby"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
:aria-expanded="overlayVisible"
|
||||||
|
:aria-controls="id + '_list'"
|
||||||
|
:aria-activedescendant="focused ? focusedOptionId : undefined"
|
||||||
|
:aria-invalid="invalid || undefined"
|
||||||
|
@focus="onFocus"
|
||||||
|
@blur="onBlur"
|
||||||
|
@keydown="onKeyDown"
|
||||||
|
@input="onEditableInput"
|
||||||
|
v-bind="ptm('input')"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
ref="focusInput"
|
||||||
|
:id="inputId"
|
||||||
|
:class="[cx('input'), inputClass]"
|
||||||
|
:style="inputStyle"
|
||||||
|
:tabindex="!disabled ? tabindex : -1"
|
||||||
|
role="combobox"
|
||||||
|
:aria-label="ariaLabel || (label === 'p-emptylabel' ? undefined : label)"
|
||||||
|
:aria-labelledby="ariaLabelledby"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
:aria-expanded="overlayVisible"
|
||||||
|
:aria-controls="id + '_list'"
|
||||||
|
:aria-activedescendant="focused ? focusedOptionId : undefined"
|
||||||
|
:aria-disabled="disabled"
|
||||||
|
@focus="onFocus"
|
||||||
|
@blur="onBlur"
|
||||||
|
@keydown="onKeyDown"
|
||||||
|
v-bind="ptm('input')"
|
||||||
|
>
|
||||||
|
<slot name="value" :value="modelValue" :placeholder="placeholder">{{ label === 'p-emptylabel' ? ' ' : label || 'empty' }}</slot>
|
||||||
|
</span>
|
||||||
|
<slot v-if="showClear && modelValue != null" name="clearicon" :class="cx('clearIcon')" :onClick="onClearClick" :clearCallback="onClearClick">
|
||||||
|
<component :is="clearIcon ? 'i' : 'TimesIcon'" ref="clearIcon" :class="[cx('clearIcon'), clearIcon]" @click="onClearClick" v-bind="ptm('clearIcon')" data-pc-section="clearicon" />
|
||||||
|
</slot>
|
||||||
|
<div :class="cx('trigger')" v-bind="ptm('trigger')">
|
||||||
|
<slot v-if="loading" name="loadingicon" :class="cx('loadingIcon')">
|
||||||
|
<span v-if="loadingIcon" :class="[cx('loadingIcon'), 'pi-spin', loadingIcon]" aria-hidden="true" v-bind="ptm('loadingIcon')" />
|
||||||
|
<SpinnerIcon v-else :class="cx('loadingIcon')" spin aria-hidden="true" v-bind="ptm('loadingIcon')" />
|
||||||
|
</slot>
|
||||||
|
<slot v-else name="dropdownicon" :class="cx('dropdownIcon')">
|
||||||
|
<component :is="dropdownIcon ? 'span' : 'ChevronDownIcon'" :class="[cx('dropdownIcon'), dropdownIcon]" aria-hidden="true" v-bind="ptm('dropdownIcon')" />
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<Portal :appendTo="appendTo">
|
||||||
|
<transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave" v-bind="ptm('transition')">
|
||||||
|
<div v-if="overlayVisible" :ref="overlayRef" :class="[cx('panel'), panelClass]" :style="panelStyle" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="ptm('panel')">
|
||||||
|
<span
|
||||||
|
ref="firstHiddenFocusableElementOnOverlay"
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="p-hidden-accessible p-hidden-focusable"
|
||||||
|
:tabindex="0"
|
||||||
|
@focus="onFirstHiddenFocus"
|
||||||
|
v-bind="ptm('hiddenFirstFocusableEl')"
|
||||||
|
:data-p-hidden-accessible="true"
|
||||||
|
:data-p-hidden-focusable="true"
|
||||||
|
></span>
|
||||||
|
<slot name="header" :value="modelValue" :options="visibleOptions"></slot>
|
||||||
|
<div v-if="filter" :class="cx('header')" v-bind="ptm('header')">
|
||||||
|
<div :class="cx('filterContainer')" v-bind="ptm('filterContainer')">
|
||||||
|
<input
|
||||||
|
ref="filterInput"
|
||||||
|
type="text"
|
||||||
|
:value="filterValue"
|
||||||
|
@vue:mounted="onFilterUpdated"
|
||||||
|
@vue:updated="onFilterUpdated"
|
||||||
|
:class="cx('filterInput')"
|
||||||
|
:placeholder="filterPlaceholder"
|
||||||
|
role="searchbox"
|
||||||
|
autocomplete="off"
|
||||||
|
:aria-owns="id + '_list'"
|
||||||
|
:aria-activedescendant="focusedOptionId"
|
||||||
|
@keydown="onFilterKeyDown"
|
||||||
|
@blur="onFilterBlur"
|
||||||
|
@input="onFilterChange"
|
||||||
|
v-bind="ptm('filterInput')"
|
||||||
|
/>
|
||||||
|
<slot name="filtericon" :class="cx('filterIcon')">
|
||||||
|
<component :is="filterIcon ? 'span' : 'SearchIcon'" :class="[cx('filterIcon'), filterIcon]" v-bind="ptm('filterIcon')" />
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<span role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenFilterResult')" :data-p-hidden-accessible="true">
|
||||||
|
{{ filterResultMessageText }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div :class="cx('wrapper')" :style="{ 'max-height': virtualScrollerDisabled ? scrollHeight : '' }" v-bind="ptm('wrapper')">
|
||||||
|
<VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :items="visibleOptions" :style="{ height: scrollHeight }" :tabindex="-1" :disabled="virtualScrollerDisabled" :pt="ptm('virtualScroller')">
|
||||||
|
<template v-slot:content="{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }">
|
||||||
|
<ul :ref="(el) => listRef(el, contentRef)" :id="id + '_list'" :class="[cx('list'), styleClass]" :style="contentStyle" role="listbox" v-bind="ptm('list')">
|
||||||
|
<template v-for="(option, i) of items" :key="getOptionRenderKey(option, getOptionIndex(i, getItemOptions))">
|
||||||
|
<li v-if="isOptionGroup(option)" :id="id + '_' + getOptionIndex(i, getItemOptions)" :style="{ height: itemSize ? itemSize + 'px' : undefined }" :class="cx('itemGroup')" role="option" v-bind="ptm('itemGroup')">
|
||||||
|
<slot name="optiongroup" :option="option.optionGroup" :index="getOptionIndex(i, getItemOptions)">
|
||||||
|
<span :class="cx('itemGroupLabel')" v-bind="ptm('itemGroupLabel')">{{ getOptionGroupLabel(option.optionGroup) }}</span>
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-else
|
||||||
|
:id="id + '_' + getOptionIndex(i, getItemOptions)"
|
||||||
|
v-ripple
|
||||||
|
:class="cx('item', { option, focusedOption: getOptionIndex(i, getItemOptions) })"
|
||||||
|
:style="{ height: itemSize ? itemSize + 'px' : undefined }"
|
||||||
|
role="option"
|
||||||
|
:aria-label="getOptionLabel(option)"
|
||||||
|
:aria-selected="isSelected(option)"
|
||||||
|
:aria-disabled="isOptionDisabled(option)"
|
||||||
|
:aria-setsize="ariaSetSize"
|
||||||
|
:aria-posinset="getAriaPosInset(getOptionIndex(i, getItemOptions))"
|
||||||
|
@click="onOptionSelect($event, option)"
|
||||||
|
@mousemove="onOptionMouseMove($event, getOptionIndex(i, getItemOptions))"
|
||||||
|
:data-p-highlight="isSelected(option)"
|
||||||
|
:data-p-focused="focusedOptionIndex === getOptionIndex(i, getItemOptions)"
|
||||||
|
:data-p-disabled="isOptionDisabled(option)"
|
||||||
|
v-bind="getPTItemOptions(option, getItemOptions, i, 'item')"
|
||||||
|
>
|
||||||
|
<template v-if="checkmark">
|
||||||
|
<CheckIcon v-if="isSelected(option)" :class="cx('checkIcon')" v-bind="ptm('checkIcon')" />
|
||||||
|
<BlankIcon v-else :class="cx('blankIcon')" v-bind="ptm('blankIcon')" />
|
||||||
|
</template>
|
||||||
|
<slot name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">
|
||||||
|
<span :class="cx('itemLabel')" v-bind="ptm('itemLabel')">{{ getOptionLabel(option) }}</span>
|
||||||
|
</slot>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
<li v-if="filterValue && (!items || (items && items.length === 0))" :class="cx('emptyMessage')" role="option" v-bind="ptm('emptyMessage')" :data-p-hidden-accessible="true">
|
||||||
|
<slot name="emptyfilter">{{ emptyFilterMessageText }}</slot>
|
||||||
|
</li>
|
||||||
|
<li v-else-if="!options || (options && options.length === 0)" :class="cx('emptyMessage')" role="option" v-bind="ptm('emptyMessage')" :data-p-hidden-accessible="true">
|
||||||
|
<slot name="empty">{{ emptyMessageText }}</slot>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
<template v-if="$slots.loader" v-slot:loader="{ options }">
|
||||||
|
<slot name="loader" :options="options"></slot>
|
||||||
|
</template>
|
||||||
|
</VirtualScroller>
|
||||||
|
</div>
|
||||||
|
<slot name="footer" :value="modelValue" :options="visibleOptions"></slot>
|
||||||
|
<span v-if="!options || (options && options.length === 0)" role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenEmptyMessage')" :data-p-hidden-accessible="true">
|
||||||
|
{{ emptyMessageText }}
|
||||||
|
</span>
|
||||||
|
<span role="status" aria-live="polite" class="p-hidden-accessible" v-bind="ptm('hiddenSelectedMessage')" :data-p-hidden-accessible="true">
|
||||||
|
{{ selectedMessageText }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
ref="lastHiddenFocusableElementOnOverlay"
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
class="p-hidden-accessible p-hidden-focusable"
|
||||||
|
:tabindex="0"
|
||||||
|
@focus="onLastHiddenFocus"
|
||||||
|
v-bind="ptm('hiddenLastFocusableEl')"
|
||||||
|
:data-p-hidden-accessible="true"
|
||||||
|
:data-p-hidden-focusable="true"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</Portal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { FilterService } from 'primevue/api';
|
||||||
|
import BlankIcon from 'primevue/icons/blank';
|
||||||
|
import CheckIcon from 'primevue/icons/check';
|
||||||
|
import ChevronDownIcon from 'primevue/icons/chevrondown';
|
||||||
|
import SearchIcon from 'primevue/icons/search';
|
||||||
|
import SpinnerIcon from 'primevue/icons/spinner';
|
||||||
|
import TimesIcon from 'primevue/icons/times';
|
||||||
|
import OverlayEventBus from 'primevue/overlayeventbus';
|
||||||
|
import Portal from 'primevue/portal';
|
||||||
|
import Ripple from 'primevue/ripple';
|
||||||
|
import { ConnectedOverlayScrollHandler, DomHandler, ObjectUtils, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
|
||||||
|
import VirtualScroller from 'primevue/virtualscroller';
|
||||||
|
import BaseSelect from './BaseSelect.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Select',
|
||||||
|
extends: BaseSelect,
|
||||||
|
inheritAttrs: false,
|
||||||
|
emits: ['update:modelValue', 'change', 'focus', 'blur', 'before-show', 'before-hide', 'show', 'hide', 'filter'],
|
||||||
|
outsideClickListener: null,
|
||||||
|
scrollHandler: null,
|
||||||
|
resizeListener: null,
|
||||||
|
labelClickListener: null,
|
||||||
|
overlay: null,
|
||||||
|
list: null,
|
||||||
|
virtualScroller: null,
|
||||||
|
searchTimeout: null,
|
||||||
|
searchValue: null,
|
||||||
|
isModelValueChanged: false,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: this.$attrs.id,
|
||||||
|
clicked: false,
|
||||||
|
focused: false,
|
||||||
|
focusedOptionIndex: -1,
|
||||||
|
filterValue: null,
|
||||||
|
overlayVisible: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$attrs.id': function (newValue) {
|
||||||
|
this.id = newValue || UniqueComponentId();
|
||||||
|
},
|
||||||
|
modelValue() {
|
||||||
|
this.isModelValueChanged = true;
|
||||||
|
},
|
||||||
|
options() {
|
||||||
|
this.autoUpdateModel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.id = this.id || UniqueComponentId();
|
||||||
|
this.autoUpdateModel();
|
||||||
|
this.bindLabelClickListener();
|
||||||
|
},
|
||||||
|
updated() {
|
||||||
|
if (this.overlayVisible && this.isModelValueChanged) {
|
||||||
|
this.scrollInView(this.findSelectedOptionIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isModelValueChanged = false;
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.unbindOutsideClickListener();
|
||||||
|
this.unbindResizeListener();
|
||||||
|
this.unbindLabelClickListener();
|
||||||
|
|
||||||
|
if (this.scrollHandler) {
|
||||||
|
this.scrollHandler.destroy();
|
||||||
|
this.scrollHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.overlay) {
|
||||||
|
ZIndexUtils.clear(this.overlay);
|
||||||
|
this.overlay = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getOptionIndex(index, fn) {
|
||||||
|
return this.virtualScrollerDisabled ? index : fn && fn(index)['index'];
|
||||||
|
},
|
||||||
|
getOptionLabel(option) {
|
||||||
|
return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option;
|
||||||
|
},
|
||||||
|
getOptionValue(option) {
|
||||||
|
return this.optionValue ? ObjectUtils.resolveFieldData(option, this.optionValue) : option;
|
||||||
|
},
|
||||||
|
getOptionRenderKey(option, index) {
|
||||||
|
return (this.dataKey ? ObjectUtils.resolveFieldData(option, this.dataKey) : this.getOptionLabel(option)) + '_' + index;
|
||||||
|
},
|
||||||
|
getPTItemOptions(option, itemOptions, index, key) {
|
||||||
|
return this.ptm(key, {
|
||||||
|
context: {
|
||||||
|
option,
|
||||||
|
index,
|
||||||
|
selected: this.isSelected(option),
|
||||||
|
focused: this.focusedOptionIndex === this.getOptionIndex(index, itemOptions),
|
||||||
|
disabled: this.isOptionDisabled(option)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isOptionDisabled(option) {
|
||||||
|
return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
|
||||||
|
},
|
||||||
|
isOptionGroup(option) {
|
||||||
|
return this.optionGroupLabel && option.optionGroup && option.group;
|
||||||
|
},
|
||||||
|
getOptionGroupLabel(optionGroup) {
|
||||||
|
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel);
|
||||||
|
},
|
||||||
|
getOptionGroupChildren(optionGroup) {
|
||||||
|
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren);
|
||||||
|
},
|
||||||
|
getAriaPosInset(index) {
|
||||||
|
return (this.optionGroupLabel ? index - this.visibleOptions.slice(0, index).filter((option) => this.isOptionGroup(option)).length : index) + 1;
|
||||||
|
},
|
||||||
|
show(isFocus) {
|
||||||
|
this.$emit('before-show');
|
||||||
|
this.overlayVisible = true;
|
||||||
|
this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.editable ? -1 : this.findSelectedOptionIndex();
|
||||||
|
|
||||||
|
isFocus && DomHandler.focus(this.$refs.focusInput);
|
||||||
|
},
|
||||||
|
hide(isFocus) {
|
||||||
|
const _hide = () => {
|
||||||
|
this.$emit('before-hide');
|
||||||
|
this.overlayVisible = false;
|
||||||
|
this.clicked = false;
|
||||||
|
this.focusedOptionIndex = -1;
|
||||||
|
this.searchValue = '';
|
||||||
|
|
||||||
|
this.resetFilterOnHide && (this.filterValue = null);
|
||||||
|
isFocus && DomHandler.focus(this.$refs.focusInput);
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
_hide();
|
||||||
|
}, 0); // For ScreenReaders
|
||||||
|
},
|
||||||
|
onFocus(event) {
|
||||||
|
if (this.disabled) {
|
||||||
|
// For ScreenReaders
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.focused = true;
|
||||||
|
|
||||||
|
if (this.overlayVisible) {
|
||||||
|
this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.editable ? -1 : this.findSelectedOptionIndex();
|
||||||
|
this.scrollInView(this.focusedOptionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('focus', event);
|
||||||
|
},
|
||||||
|
onBlur(event) {
|
||||||
|
this.focused = false;
|
||||||
|
this.focusedOptionIndex = -1;
|
||||||
|
this.searchValue = '';
|
||||||
|
this.$emit('blur', event);
|
||||||
|
},
|
||||||
|
onKeyDown(event) {
|
||||||
|
if (this.disabled || DomHandler.isAndroid()) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaKey = event.metaKey || event.ctrlKey;
|
||||||
|
|
||||||
|
switch (event.code) {
|
||||||
|
case 'ArrowDown':
|
||||||
|
this.onArrowDownKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowUp':
|
||||||
|
this.onArrowUpKey(event, this.editable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowLeft':
|
||||||
|
case 'ArrowRight':
|
||||||
|
this.onArrowLeftKey(event, this.editable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Delete':
|
||||||
|
this.onDeleteKey(event);
|
||||||
|
|
||||||
|
case 'Home':
|
||||||
|
this.onHomeKey(event, this.editable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'End':
|
||||||
|
this.onEndKey(event, this.editable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PageDown':
|
||||||
|
this.onPageDownKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PageUp':
|
||||||
|
this.onPageUpKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Space':
|
||||||
|
this.onSpaceKey(event, this.editable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Enter':
|
||||||
|
case 'NumpadEnter':
|
||||||
|
this.onEnterKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Escape':
|
||||||
|
this.onEscapeKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Tab':
|
||||||
|
this.onTabKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Backspace':
|
||||||
|
this.onBackspaceKey(event, this.editable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ShiftLeft':
|
||||||
|
case 'ShiftRight':
|
||||||
|
//NOOP
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (!metaKey && ObjectUtils.isPrintableCharacter(event.key)) {
|
||||||
|
!this.overlayVisible && this.show();
|
||||||
|
!this.editable && this.searchOptions(event, event.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clicked = false;
|
||||||
|
},
|
||||||
|
onEditableInput(event) {
|
||||||
|
const value = event.target.value;
|
||||||
|
|
||||||
|
this.searchValue = '';
|
||||||
|
const matched = this.searchOptions(event, value);
|
||||||
|
|
||||||
|
!matched && (this.focusedOptionIndex = -1);
|
||||||
|
|
||||||
|
this.updateModel(event, value);
|
||||||
|
|
||||||
|
!this.overlayVisible && ObjectUtils.isNotEmpty(value) && this.show();
|
||||||
|
},
|
||||||
|
onContainerClick(event) {
|
||||||
|
if (this.disabled || this.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.tagName === 'INPUT' || event.target.getAttribute('data-pc-section') === 'clearicon' || event.target.closest('[data-pc-section="clearicon"]')) {
|
||||||
|
return;
|
||||||
|
} else if (!this.overlay || !this.overlay.contains(event.target)) {
|
||||||
|
this.overlayVisible ? this.hide(true) : this.show(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clicked = true;
|
||||||
|
},
|
||||||
|
onClearClick(event) {
|
||||||
|
this.updateModel(event, null);
|
||||||
|
this.resetFilterOnClear && (this.filterValue = null);
|
||||||
|
},
|
||||||
|
onFirstHiddenFocus(event) {
|
||||||
|
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getFirstFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
|
||||||
|
|
||||||
|
DomHandler.focus(focusableEl);
|
||||||
|
},
|
||||||
|
onLastHiddenFocus(event) {
|
||||||
|
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getLastFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
|
||||||
|
|
||||||
|
DomHandler.focus(focusableEl);
|
||||||
|
},
|
||||||
|
onOptionSelect(event, option, isHide = true) {
|
||||||
|
const value = this.getOptionValue(option);
|
||||||
|
|
||||||
|
this.updateModel(event, value);
|
||||||
|
isHide && this.hide(true);
|
||||||
|
},
|
||||||
|
onOptionMouseMove(event, index) {
|
||||||
|
if (this.focusOnHover) {
|
||||||
|
this.changeFocusedOptionIndex(event, index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFilterChange(event) {
|
||||||
|
const value = event.target.value;
|
||||||
|
|
||||||
|
this.filterValue = value;
|
||||||
|
this.focusedOptionIndex = -1;
|
||||||
|
this.$emit('filter', { originalEvent: event, value });
|
||||||
|
|
||||||
|
!this.virtualScrollerDisabled && this.virtualScroller.scrollToIndex(0);
|
||||||
|
},
|
||||||
|
onFilterKeyDown(event) {
|
||||||
|
switch (event.code) {
|
||||||
|
case 'ArrowDown':
|
||||||
|
this.onArrowDownKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowUp':
|
||||||
|
this.onArrowUpKey(event, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ArrowLeft':
|
||||||
|
case 'ArrowRight':
|
||||||
|
this.onArrowLeftKey(event, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Home':
|
||||||
|
this.onHomeKey(event, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'End':
|
||||||
|
this.onEndKey(event, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Enter':
|
||||||
|
case 'NumpadEnter':
|
||||||
|
this.onEnterKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Escape':
|
||||||
|
this.onEscapeKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Tab':
|
||||||
|
this.onTabKey(event, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFilterBlur() {
|
||||||
|
this.focusedOptionIndex = -1;
|
||||||
|
},
|
||||||
|
onFilterUpdated() {
|
||||||
|
if (this.overlayVisible) {
|
||||||
|
this.alignOverlay();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onOverlayClick(event) {
|
||||||
|
OverlayEventBus.emit('overlay-click', {
|
||||||
|
originalEvent: event,
|
||||||
|
target: this.$el
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onOverlayKeyDown(event) {
|
||||||
|
switch (event.code) {
|
||||||
|
case 'Escape':
|
||||||
|
this.onEscapeKey(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDeleteKey(event) {
|
||||||
|
if (this.showClear) {
|
||||||
|
this.updateModel(event, null);
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onArrowDownKey(event) {
|
||||||
|
if (!this.overlayVisible) {
|
||||||
|
this.show();
|
||||||
|
this.editable && this.changeFocusedOptionIndex(event, this.findSelectedOptionIndex());
|
||||||
|
} else {
|
||||||
|
const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();
|
||||||
|
|
||||||
|
this.changeFocusedOptionIndex(event, optionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
onArrowUpKey(event, pressedInInputText = false) {
|
||||||
|
if (event.altKey && !pressedInInputText) {
|
||||||
|
if (this.focusedOptionIndex !== -1) {
|
||||||
|
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.overlayVisible && this.hide();
|
||||||
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
|
const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();
|
||||||
|
|
||||||
|
this.changeFocusedOptionIndex(event, optionIndex);
|
||||||
|
|
||||||
|
!this.overlayVisible && this.show();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onArrowLeftKey(event, pressedInInputText = false) {
|
||||||
|
pressedInInputText && (this.focusedOptionIndex = -1);
|
||||||
|
},
|
||||||
|
onHomeKey(event, pressedInInputText = false) {
|
||||||
|
if (pressedInInputText) {
|
||||||
|
event.currentTarget.setSelectionRange(0, 0);
|
||||||
|
this.focusedOptionIndex = -1;
|
||||||
|
} else {
|
||||||
|
this.changeFocusedOptionIndex(event, this.findFirstOptionIndex());
|
||||||
|
|
||||||
|
!this.overlayVisible && this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
onEndKey(event, pressedInInputText = false) {
|
||||||
|
if (pressedInInputText) {
|
||||||
|
const target = event.currentTarget;
|
||||||
|
const len = target.value.length;
|
||||||
|
|
||||||
|
target.setSelectionRange(len, len);
|
||||||
|
this.focusedOptionIndex = -1;
|
||||||
|
} else {
|
||||||
|
this.changeFocusedOptionIndex(event, this.findLastOptionIndex());
|
||||||
|
|
||||||
|
!this.overlayVisible && this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
onPageUpKey(event) {
|
||||||
|
this.scrollInView(0);
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
onPageDownKey(event) {
|
||||||
|
this.scrollInView(this.visibleOptions.length - 1);
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
onEnterKey(event) {
|
||||||
|
if (!this.overlayVisible) {
|
||||||
|
this.focusedOptionIndex = -1; // reset
|
||||||
|
this.onArrowDownKey(event);
|
||||||
|
} else {
|
||||||
|
if (this.focusedOptionIndex !== -1) {
|
||||||
|
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
onSpaceKey(event, pressedInInputText = false) {
|
||||||
|
!pressedInInputText && this.onEnterKey(event);
|
||||||
|
},
|
||||||
|
onEscapeKey(event) {
|
||||||
|
this.overlayVisible && this.hide(true);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation(); //@todo will be changed next versionss
|
||||||
|
},
|
||||||
|
onTabKey(event, pressedInInputText = false) {
|
||||||
|
if (!pressedInInputText) {
|
||||||
|
if (this.overlayVisible && this.hasFocusableElements()) {
|
||||||
|
DomHandler.focus(this.$refs.firstHiddenFocusableElementOnOverlay);
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
} else {
|
||||||
|
if (this.focusedOptionIndex !== -1) {
|
||||||
|
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.overlayVisible && this.hide(this.filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBackspaceKey(event, pressedInInputText = false) {
|
||||||
|
if (pressedInInputText) {
|
||||||
|
!this.overlayVisible && this.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onOverlayEnter(el) {
|
||||||
|
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
||||||
|
|
||||||
|
DomHandler.addStyles(el, { position: 'absolute', top: '0', left: '0' });
|
||||||
|
this.alignOverlay();
|
||||||
|
this.scrollInView();
|
||||||
|
|
||||||
|
this.autoFilterFocus && DomHandler.focus(this.$refs.filterInput);
|
||||||
|
},
|
||||||
|
onOverlayAfterEnter() {
|
||||||
|
this.bindOutsideClickListener();
|
||||||
|
this.bindScrollListener();
|
||||||
|
this.bindResizeListener();
|
||||||
|
|
||||||
|
this.$emit('show');
|
||||||
|
},
|
||||||
|
onOverlayLeave() {
|
||||||
|
this.unbindOutsideClickListener();
|
||||||
|
this.unbindScrollListener();
|
||||||
|
this.unbindResizeListener();
|
||||||
|
|
||||||
|
this.$emit('hide');
|
||||||
|
this.overlay = null;
|
||||||
|
},
|
||||||
|
onOverlayAfterLeave(el) {
|
||||||
|
ZIndexUtils.clear(el);
|
||||||
|
},
|
||||||
|
alignOverlay() {
|
||||||
|
if (this.appendTo === 'self') {
|
||||||
|
DomHandler.relativePosition(this.overlay, this.$el);
|
||||||
|
} else {
|
||||||
|
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$el) + 'px';
|
||||||
|
DomHandler.absolutePosition(this.overlay, this.$el);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bindOutsideClickListener() {
|
||||||
|
if (!this.outsideClickListener) {
|
||||||
|
this.outsideClickListener = (event) => {
|
||||||
|
if (this.overlayVisible && this.overlay && !this.$el.contains(event.target) && !this.overlay.contains(event.target)) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', this.outsideClickListener);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unbindOutsideClickListener() {
|
||||||
|
if (this.outsideClickListener) {
|
||||||
|
document.removeEventListener('click', this.outsideClickListener);
|
||||||
|
this.outsideClickListener = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bindScrollListener() {
|
||||||
|
if (!this.scrollHandler) {
|
||||||
|
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.container, () => {
|
||||||
|
if (this.overlayVisible) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scrollHandler.bindScrollListener();
|
||||||
|
},
|
||||||
|
unbindScrollListener() {
|
||||||
|
if (this.scrollHandler) {
|
||||||
|
this.scrollHandler.unbindScrollListener();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bindResizeListener() {
|
||||||
|
if (!this.resizeListener) {
|
||||||
|
this.resizeListener = () => {
|
||||||
|
if (this.overlayVisible && !DomHandler.isTouchDevice()) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', this.resizeListener);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unbindResizeListener() {
|
||||||
|
if (this.resizeListener) {
|
||||||
|
window.removeEventListener('resize', this.resizeListener);
|
||||||
|
this.resizeListener = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bindLabelClickListener() {
|
||||||
|
if (!this.editable && !this.labelClickListener) {
|
||||||
|
const label = document.querySelector(`label[for="${this.inputId}"]`);
|
||||||
|
|
||||||
|
if (label && DomHandler.isVisible(label)) {
|
||||||
|
this.labelClickListener = () => {
|
||||||
|
DomHandler.focus(this.$refs.focusInput);
|
||||||
|
};
|
||||||
|
|
||||||
|
label.addEventListener('click', this.labelClickListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unbindLabelClickListener() {
|
||||||
|
if (this.labelClickListener) {
|
||||||
|
const label = document.querySelector(`label[for="${this.inputId}"]`);
|
||||||
|
|
||||||
|
if (label && DomHandler.isVisible(label)) {
|
||||||
|
label.removeEventListener('click', this.labelClickListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasFocusableElements() {
|
||||||
|
return DomHandler.getFocusableElements(this.overlay, ':not([data-p-hidden-focusable="true"])').length > 0;
|
||||||
|
},
|
||||||
|
isOptionMatched(option) {
|
||||||
|
return this.isValidOption(option) && typeof this.getOptionLabel(option) === 'string' && this.getOptionLabel(option)?.toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));
|
||||||
|
},
|
||||||
|
isValidOption(option) {
|
||||||
|
return ObjectUtils.isNotEmpty(option) && !(this.isOptionDisabled(option) || this.isOptionGroup(option));
|
||||||
|
},
|
||||||
|
isValidSelectedOption(option) {
|
||||||
|
return this.isValidOption(option) && this.isSelected(option);
|
||||||
|
},
|
||||||
|
isSelected(option) {
|
||||||
|
return this.isValidOption(option) && ObjectUtils.equals(this.modelValue, this.getOptionValue(option), this.equalityKey);
|
||||||
|
},
|
||||||
|
findFirstOptionIndex() {
|
||||||
|
return this.visibleOptions.findIndex((option) => this.isValidOption(option));
|
||||||
|
},
|
||||||
|
findLastOptionIndex() {
|
||||||
|
return ObjectUtils.findLastIndex(this.visibleOptions, (option) => this.isValidOption(option));
|
||||||
|
},
|
||||||
|
findNextOptionIndex(index) {
|
||||||
|
const matchedOptionIndex = index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((option) => this.isValidOption(option)) : -1;
|
||||||
|
|
||||||
|
return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
|
||||||
|
},
|
||||||
|
findPrevOptionIndex(index) {
|
||||||
|
const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidOption(option)) : -1;
|
||||||
|
|
||||||
|
return matchedOptionIndex > -1 ? matchedOptionIndex : index;
|
||||||
|
},
|
||||||
|
findSelectedOptionIndex() {
|
||||||
|
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
|
||||||
|
},
|
||||||
|
findFirstFocusedOptionIndex() {
|
||||||
|
const selectedIndex = this.findSelectedOptionIndex();
|
||||||
|
|
||||||
|
return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
|
||||||
|
},
|
||||||
|
findLastFocusedOptionIndex() {
|
||||||
|
const selectedIndex = this.findSelectedOptionIndex();
|
||||||
|
|
||||||
|
return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
|
||||||
|
},
|
||||||
|
searchOptions(event, char) {
|
||||||
|
this.searchValue = (this.searchValue || '') + char;
|
||||||
|
|
||||||
|
let optionIndex = -1;
|
||||||
|
let matched = false;
|
||||||
|
|
||||||
|
if (ObjectUtils.isNotEmpty(this.searchValue)) {
|
||||||
|
if (this.focusedOptionIndex !== -1) {
|
||||||
|
optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
|
||||||
|
optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
|
||||||
|
} else {
|
||||||
|
optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionIndex !== -1) {
|
||||||
|
matched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionIndex === -1 && this.focusedOptionIndex === -1) {
|
||||||
|
optionIndex = this.findFirstFocusedOptionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionIndex !== -1) {
|
||||||
|
this.changeFocusedOptionIndex(event, optionIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.searchTimeout) {
|
||||||
|
clearTimeout(this.searchTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchTimeout = setTimeout(() => {
|
||||||
|
this.searchValue = '';
|
||||||
|
this.searchTimeout = null;
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
},
|
||||||
|
changeFocusedOptionIndex(event, index) {
|
||||||
|
if (this.focusedOptionIndex !== index) {
|
||||||
|
this.focusedOptionIndex = index;
|
||||||
|
this.scrollInView();
|
||||||
|
|
||||||
|
if (this.selectOnFocus) {
|
||||||
|
this.onOptionSelect(event, this.visibleOptions[index], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollInView(index = -1) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
|
||||||
|
const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
|
||||||
|
} else if (!this.virtualScrollerDisabled) {
|
||||||
|
this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autoUpdateModel() {
|
||||||
|
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
|
||||||
|
this.focusedOptionIndex = this.findFirstFocusedOptionIndex();
|
||||||
|
this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex], false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateModel(event, value) {
|
||||||
|
this.$emit('update:modelValue', value);
|
||||||
|
this.$emit('change', { originalEvent: event, value });
|
||||||
|
},
|
||||||
|
flatOptions(options) {
|
||||||
|
return (options || []).reduce((result, option, index) => {
|
||||||
|
result.push({ optionGroup: option, group: true, index });
|
||||||
|
|
||||||
|
const optionGroupChildren = this.getOptionGroupChildren(option);
|
||||||
|
|
||||||
|
optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
|
overlayRef(el) {
|
||||||
|
this.overlay = el;
|
||||||
|
},
|
||||||
|
listRef(el, contentRef) {
|
||||||
|
this.list = el;
|
||||||
|
contentRef && contentRef(el); // For VirtualScroller
|
||||||
|
},
|
||||||
|
virtualScrollerRef(el) {
|
||||||
|
this.virtualScroller = el;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
visibleOptions() {
|
||||||
|
const options = this.optionGroupLabel ? this.flatOptions(this.options) : this.options || [];
|
||||||
|
|
||||||
|
if (this.filterValue) {
|
||||||
|
const filteredOptions = FilterService.filter(options, this.searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
|
||||||
|
|
||||||
|
if (this.optionGroupLabel) {
|
||||||
|
const optionGroups = this.options || [];
|
||||||
|
const filtered = [];
|
||||||
|
|
||||||
|
optionGroups.forEach((group) => {
|
||||||
|
const groupChildren = this.getOptionGroupChildren(group);
|
||||||
|
const filteredItems = groupChildren.filter((item) => filteredOptions.includes(item));
|
||||||
|
|
||||||
|
if (filteredItems.length > 0) filtered.push({ ...group, [typeof this.optionGroupChildren === 'string' ? this.optionGroupChildren : 'items']: [...filteredItems] });
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.flatOptions(filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
},
|
||||||
|
hasSelectedOption() {
|
||||||
|
return ObjectUtils.isNotEmpty(this.modelValue);
|
||||||
|
},
|
||||||
|
label() {
|
||||||
|
const selectedOptionIndex = this.findSelectedOptionIndex();
|
||||||
|
|
||||||
|
return selectedOptionIndex !== -1 ? this.getOptionLabel(this.visibleOptions[selectedOptionIndex]) : this.placeholder || 'p-emptylabel';
|
||||||
|
},
|
||||||
|
editableInputValue() {
|
||||||
|
const selectedOptionIndex = this.findSelectedOptionIndex();
|
||||||
|
|
||||||
|
return selectedOptionIndex !== -1 ? this.getOptionLabel(this.visibleOptions[selectedOptionIndex]) : this.modelValue || '';
|
||||||
|
},
|
||||||
|
equalityKey() {
|
||||||
|
return this.optionValue ? null : this.dataKey;
|
||||||
|
},
|
||||||
|
searchFields() {
|
||||||
|
return this.filterFields || [this.optionLabel];
|
||||||
|
},
|
||||||
|
filterResultMessageText() {
|
||||||
|
return ObjectUtils.isNotEmpty(this.visibleOptions) ? this.filterMessageText.replaceAll('{0}', this.visibleOptions.length) : this.emptyFilterMessageText;
|
||||||
|
},
|
||||||
|
filterMessageText() {
|
||||||
|
return this.filterMessage || this.$primevue.config.locale.searchMessage || '';
|
||||||
|
},
|
||||||
|
emptyFilterMessageText() {
|
||||||
|
return this.emptyFilterMessage || this.$primevue.config.locale.emptySearchMessage || this.$primevue.config.locale.emptyFilterMessage || '';
|
||||||
|
},
|
||||||
|
emptyMessageText() {
|
||||||
|
return this.emptyMessage || this.$primevue.config.locale.emptyMessage || '';
|
||||||
|
},
|
||||||
|
selectionMessageText() {
|
||||||
|
return this.selectionMessage || this.$primevue.config.locale.selectionMessage || '';
|
||||||
|
},
|
||||||
|
emptySelectionMessageText() {
|
||||||
|
return this.emptySelectionMessage || this.$primevue.config.locale.emptySelectionMessage || '';
|
||||||
|
},
|
||||||
|
selectedMessageText() {
|
||||||
|
return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', '1') : this.emptySelectionMessageText;
|
||||||
|
},
|
||||||
|
focusedOptionId() {
|
||||||
|
return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null;
|
||||||
|
},
|
||||||
|
ariaSetSize() {
|
||||||
|
return this.visibleOptions.filter((option) => !this.isOptionGroup(option)).length;
|
||||||
|
},
|
||||||
|
virtualScrollerDisabled() {
|
||||||
|
return !this.virtualScrollerOptions;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
ripple: Ripple
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
VirtualScroller,
|
||||||
|
Portal,
|
||||||
|
TimesIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
SpinnerIcon,
|
||||||
|
SearchIcon,
|
||||||
|
CheckIcon,
|
||||||
|
BlankIcon
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"main": "./select.cjs.js",
|
||||||
|
"module": "./select.esm.js",
|
||||||
|
"unpkg": "./select.min.js",
|
||||||
|
"types": "./Select.d.ts",
|
||||||
|
"browser": {
|
||||||
|
"./sfc": "./Select.vue"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { BaseStyle } from '../../base/style';
|
||||||
|
|
||||||
|
export interface SelectStyle extends BaseStyle {}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import BaseStyle from 'primevue/base/style';
|
||||||
|
|
||||||
|
const classes = {
|
||||||
|
root: ({ instance, props, state }) => [
|
||||||
|
'p-select p-component p-inputwrapper',
|
||||||
|
{
|
||||||
|
'p-disabled': props.disabled,
|
||||||
|
'p-invalid': props.invalid,
|
||||||
|
'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled',
|
||||||
|
'p-focus': state.focused,
|
||||||
|
'p-inputwrapper-filled': instance.hasSelectedOption,
|
||||||
|
'p-inputwrapper-focus': state.focused || state.overlayVisible,
|
||||||
|
'p-select-open': state.overlayVisible
|
||||||
|
}
|
||||||
|
],
|
||||||
|
input: ({ instance, props }) => [
|
||||||
|
'p-select-label',
|
||||||
|
{
|
||||||
|
'p-placeholder': !props.editable && instance.label === props.placeholder,
|
||||||
|
'p-select-label-empty': !props.editable && !instance.$slots['value'] && (instance.label === 'p-emptylabel' || instance.label.length === 0)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
clearIcon: 'p-select-clear-icon',
|
||||||
|
trigger: 'p-select-dropdown',
|
||||||
|
loadingicon: 'p-select-loading-icon',
|
||||||
|
dropdownIcon: 'p-select-dropdown-icon',
|
||||||
|
panel: ({ instance }) => [
|
||||||
|
'p-select-overlay p-component',
|
||||||
|
{
|
||||||
|
'p-ripple-disabled': instance.$primevue.config.ripple === false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
header: 'p-select-header',
|
||||||
|
filterContainer: 'p-select-filter-container',
|
||||||
|
filterInput: 'p-select-filter',
|
||||||
|
filterIcon: 'p-select-filter-icon',
|
||||||
|
wrapper: 'p-select-list-container',
|
||||||
|
list: 'p-select-list',
|
||||||
|
itemGroup: 'p-select-option-group',
|
||||||
|
itemGroupLabel: 'p-select-option-group-label',
|
||||||
|
item: ({ instance, props, state, option, focusedOption }) => [
|
||||||
|
'p-select-option',
|
||||||
|
{
|
||||||
|
'p-select-option-selected': instance.isSelected(option) && props.highlightOnSelect,
|
||||||
|
'p-focus': state.focusedOptionIndex === focusedOption,
|
||||||
|
'p-disabled': instance.isOptionDisabled(option)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
itemLabel: 'p-select-option-label',
|
||||||
|
checkIcon: 'p-select-option-check-icon',
|
||||||
|
blankIcon: 'p-select-option-blank-icon',
|
||||||
|
emptyMessage: 'p-select-empty-message'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseStyle.extend({
|
||||||
|
name: 'select',
|
||||||
|
classes
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"main": "./selectstyle.cjs.js",
|
||||||
|
"module": "./selectstyle.esm.js",
|
||||||
|
"unpkg": "./selectstyle.min.js",
|
||||||
|
"types": "./SelectStyle.d.ts"
|
||||||
|
}
|
|
@ -5,38 +5,38 @@ export default {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background: ${dt('dropdown.background')};
|
background: ${dt('select.background')};
|
||||||
border: 1px solid ${dt('dropdown.border.color')};
|
border: 1px solid ${dt('select.border.color')};
|
||||||
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')};
|
||||||
border-radius: ${dt('rounded.base')};
|
border-radius: ${dt('rounded.base')};
|
||||||
outline-color: transparent;
|
outline-color: transparent;
|
||||||
box-shadow: ${dt('dropdown.box.shadow')};
|
box-shadow: ${dt('select.box.shadow')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select:not(.p-disabled):hover {
|
.p-select:not(.p-disabled):hover {
|
||||||
border-color: ${dt('dropdown.hover.border.color')};
|
border-color: ${dt('select.hover.border.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select:not(.p-disabled).p-focus {
|
.p-select:not(.p-disabled).p-focus {
|
||||||
border-color:${dt('dropdown.focus.border.color')};
|
border-color:${dt('select.focus.border.color')};
|
||||||
outline: 0 none;
|
outline: 0 none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select.p-variant-filled {
|
.p-select.p-variant-filled {
|
||||||
background: ${dt('dropdown.filled.background')};
|
background: ${dt('select.filled.background')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select.p-variant-filled.p-focus {
|
.p-select.p-variant-filled.p-focus {
|
||||||
background: ${dt('dropdown.filled.focus.background')};
|
background: ${dt('select.filled.focus.background')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select.p-invalid {
|
.p-select.p-invalid {
|
||||||
border-color: ${dt('dropdown.invalid.border.color')};
|
border-color: ${dt('select.invalid.border.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select.p-disabled {
|
.p-select.p-disabled {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: ${dt('dropdown.disabled.background')};
|
background: ${dt('select.disabled.background')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-clear-icon {
|
.p-select-clear-icon {
|
||||||
|
@ -53,7 +53,7 @@ export default {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: ${dt('dropdown.toggle.color')};
|
color: ${dt('select.toggle.color')};
|
||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
border-top-right-radius: ${dt('rounded.base')};
|
border-top-right-radius: ${dt('rounded.base')};
|
||||||
border-bottom-right-radius: ${dt('rounded.base')};
|
border-bottom-right-radius: ${dt('rounded.base')};
|
||||||
|
@ -68,14 +68,14 @@ export default {
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: ${dt('dropdown.color')};
|
color: ${dt('select.color')};
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0 none;
|
border: 0 none;
|
||||||
outline: 0 none;
|
outline: 0 none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-label.p-placeholder {
|
.p-select-label.p-placeholder {
|
||||||
color: ${dt('dropdown.placeholder.color')};
|
color: ${dt('select.placeholder.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select:has(.p-select-clear-icon) .p-select-label {
|
.p-select:has(.p-select-clear-icon) .p-select-label {
|
||||||
|
@ -83,7 +83,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select.p-disabled .p-select-label {
|
.p-select.p-disabled .p-select-label {
|
||||||
color: ${dt('dropdown.disabled.color')};
|
color: ${dt('select.disabled.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-label-empty {
|
.p-select-label-empty {
|
||||||
|
@ -103,9 +103,9 @@ input.p-select-label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: ${dt('dropdown.overlay.background')};
|
background: ${dt('select.overlay.background')};
|
||||||
color: ${dt('dropdown.overlay.color')};
|
color: ${dt('select.overlay.color')};
|
||||||
border: 1px solid ${dt('dropdown.overlay.border.color')};
|
border: 1px solid ${dt('select.overlay.border.color')};
|
||||||
border-radius: ${dt('rounded.base')};
|
border-radius: ${dt('rounded.base')};
|
||||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ input.p-select-label {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
margin-top: -0.5rem;
|
margin-top: -0.5rem;
|
||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
color: ${dt('dropdown.filter.icon.color')};
|
color: ${dt('select.filter.icon.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-list-container {
|
.p-select-list-container {
|
||||||
|
@ -140,8 +140,8 @@ input.p-select-label {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
background: ${dt('dropdown.item.group.background')};
|
background: ${dt('select.item.group.background')};
|
||||||
color: ${dt('dropdown.item.group.color')};
|
color: ${dt('select.item.group.color')};
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ input.p-select-label {
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
border: 0 none;
|
border: 0 none;
|
||||||
color: ${dt('dropdown.item.color')};
|
color: ${dt('select.item.color')};
|
||||||
background: transparent;
|
background: transparent;
|
||||||
transition: background-color ${dt('transition.duration')}, color ${dt('transition.duration')}, border-color ${dt('transition.duration')}, box-shadow ${dt('transition.duration')}, outline-color ${dt('transition.duration')};
|
transition: background-color ${dt('transition.duration')}, color ${dt('transition.duration')}, border-color ${dt('transition.duration')}, box-shadow ${dt('transition.duration')}, outline-color ${dt('transition.duration')};
|
||||||
border-radius: ${dt('rounded.sm')};
|
border-radius: ${dt('rounded.sm')};
|
||||||
|
@ -178,25 +178,25 @@ input.p-select-label {
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-option:not(.p-select-option-selected):not(.p-disabled).p-focus {
|
.p-select-option:not(.p-select-option-selected):not(.p-disabled).p-focus {
|
||||||
background: ${dt('dropdown.item.focus.background')};
|
background: ${dt('select.item.focus.background')};
|
||||||
color: ${dt('dropdown.item.focus.color')};
|
color: ${dt('select.item.focus.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-option.p-select-option-selected {
|
.p-select-option.p-select-option-selected {
|
||||||
background: ${dt('dropdown.item.selected.background')};
|
background: ${dt('select.item.selected.background')};
|
||||||
color: ${dt('dropdown.item.selected.color')};
|
color: ${dt('select.item.selected.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-option.p-select-option-selected.p-focus {
|
.p-select-option.p-select-option-selected.p-focus {
|
||||||
background: ${dt('dropdown.item.selected.focus.background')};
|
background: ${dt('select.item.selected.focus.background')};
|
||||||
color: ${dt('dropdown.item.selected.focus.color')};
|
color: ${dt('select.item.selected.focus.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-option-check-icon {
|
.p-select-option-check-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-left: -0.375rem;
|
margin-left: -0.375rem;
|
||||||
margin-right: 0.375rem;
|
margin-right: 0.375rem;
|
||||||
color: ${dt('dropdown.checkmark.color')};
|
color: ${dt('select.checkmark.color')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-select-empty-message {
|
.p-select-empty-message {
|
Loading…
Reference in New Issue