Components added. Build issues fixed

This commit is contained in:
Bahadir Sofuoglu 2022-09-14 14:26:01 +03:00
parent 5b66ed1093
commit 18c3721848
344 changed files with 12446 additions and 8758 deletions

View file

@ -18,6 +18,11 @@ export interface AccordionTabOpenEvent {
*/ */
export interface AccordionTabCloseEvent extends AccordionTabOpenEvent {} export interface AccordionTabCloseEvent extends AccordionTabOpenEvent {}
/**
* @extends AccordionTabOpenEvent
*/
export interface AccordionClickEvent extends AccordionTabOpenEvent {}
export interface AccordionProps { export interface AccordionProps {
/** /**
* When enabled, multiple tabs can be activated at the same time. * When enabled, multiple tabs can be activated at the same time.
@ -39,6 +44,14 @@ export interface AccordionProps {
* Icon of an expanded tab. * Icon of an expanded tab.
*/ */
collapseIcon?: string | undefined; collapseIcon?: string | undefined;
/**
* Index of the element in tabbing order.
*/
tabindex?: number | undefined;
/**
* When enabled, the focused tab is activated.
*/
selectOnFocus?: boolean | undefined;
} }
export interface AccordionSlots { export interface AccordionSlots {
@ -64,13 +77,18 @@ export declare type AccordionEmits = {
* @param {AccordionTabCloseEvent} event - Custom tab close event. * @param {AccordionTabCloseEvent} event - Custom tab close event.
*/ */
'tab-close': (event: AccordionTabCloseEvent) => void; 'tab-close': (event: AccordionTabCloseEvent) => void;
} /**
* Callback to invoke when an active tab is clicked.
* @param {AccordionClickEvent} event - Custom tab click event.
*/
'tab-click': (event: AccordionClickEvent) => void;
};
declare class Accordion extends ClassComponent<AccordionProps, AccordionSlots, AccordionEmits> {} declare class Accordion extends ClassComponent<AccordionProps, AccordionSlots, AccordionEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Accordion: GlobalComponentConstructor<Accordion> Accordion: GlobalComponentConstructor<Accordion>;
} }
} }
@ -84,7 +102,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Accordion](https://www.primefaces.org/primevue/showcase/#/accordion) * - [Accordion](https://www.primefaces.org/primevue/accordion)
* *
*/ */
export default Accordion; export default Accordion;

View file

@ -1,17 +1,35 @@
<template> <template>
<div class="p-accordion p-component"> <div class="p-accordion p-component">
<div v-for="(tab,i) of tabs" :key="getKey(tab,i)" :class="getTabClass(i)"> <div v-for="(tab, i) of tabs" :key="getKey(tab, i)" :class="getTabClass(i)" :data-index="i">
<div :class="getTabHeaderClass(tab, i)"> <div :style="getTabProp(tab, 'headerStyle')" :class="getTabHeaderClass(tab, i)" v-bind="getTabProp(tab, 'headerProps')">
<a role="tab" class="p-accordion-header-link" @click="onTabClick($event, tab, i)" @keydown="onTabKeydown($event, tab, i)" :tabindex="isTabDisabled(tab) ? null : '0'" <a
:aria-expanded="isTabActive(i)" :id="getTabAriaId(i) + '_header'" :aria-controls="getTabAriaId(i) + '_content'"> :id="getTabHeaderActionId(i)"
<span :class="isTabActive(i) ? getHeaderCollapseIcon() : getHeaderExpandIcon()"></span> class="p-accordion-header-link p-accordion-header-action"
<span class="p-accordion-header-text" v-if="tab.props && tab.props.header">{{tab.props.header}}</span> :tabindex="getTabProp(tab, 'disabled') ? -1 : tabindex"
<component :is="tab.children.header" v-if="tab.children && tab.children.header"></component> role="button"
:aria-disabled="getTabProp(tab, 'disabled')"
:aria-expanded="isTabActive(i)"
:aria-controls="getTabContentId(i)"
@click="onTabClick($event, tab, i)"
@keydown="onTabKeyDown($event, tab, i)"
v-bind="getTabProp(tab, 'headerActionProps')"
>
<span :class="getTabHeaderIconClass(i)" aria-hidden="true"></span>
<span v-if="tab.props && tab.props.header" class="p-accordion-header-text">{{ tab.props.header }}</span>
<component v-if="tab.children && tab.children.header" :is="tab.children.header"></component>
</a> </a>
</div> </div>
<transition name="p-toggleable-content"> <transition name="p-toggleable-content">
<div class="p-toggleable-content" v-if="lazy ? isTabActive(i) : true" v-show="lazy ? true: isTabActive(i)" <div
role="region" :id="getTabAriaId(i) + '_content'" :aria-labelledby="getTabAriaId(i) + '_header'"> v-if="lazy ? isTabActive(i) : true"
v-show="lazy ? true : isTabActive(i)"
:id="getTabContentId(i)"
:style="getTabProp(tab, 'contentStyle')"
:class="getTabContentClass(tab)"
role="region"
:aria-labelledby="getTabHeaderActionId(i)"
v-bind="getTabProp(tab, 'contentProps')"
>
<div class="p-accordion-content"> <div class="p-accordion-content">
<component :is="tab"></component> <component :is="tab"></component>
</div> </div>
@ -22,11 +40,12 @@
</template> </template>
<script> <script>
import {UniqueComponentId} from 'primevue/utils'; import { UniqueComponentId, DomHandler } from 'primevue/utils';
import Ripple from 'primevue/ripple';
export default { export default {
name: 'Accordion', name: 'Accordion',
emits: ['tab-close', 'tab-open', 'update:activeIndex'], emits: ['update:activeIndex', 'tab-open', 'tab-close', 'tab-click'],
props: { props: {
multiple: { multiple: {
type: Boolean, type: Boolean,
@ -47,12 +66,20 @@ export default {
collapseIcon: { collapseIcon: {
type: String, type: String,
default: 'pi-chevron-down' default: 'pi-chevron-down'
},
tabindex: {
type: Number,
default: 0
},
selectOnFocus: {
type: Boolean,
default: false
} }
}, },
data() { data() {
return { return {
d_activeIndex: this.activeIndex d_activeIndex: this.activeIndex
} };
}, },
watch: { watch: {
activeIndex(newValue) { activeIndex(newValue) {
@ -60,97 +87,187 @@ export default {
} }
}, },
methods: { methods: {
onTabClick(event, tab, i) { isAccordionTab(child) {
if (!this.isTabDisabled(tab)) { return child.type.name === 'AccordionTab';
const active = this.isTabActive(i); },
isTabActive(index) {
return this.multiple ? this.d_activeIndex && this.d_activeIndex.includes(index) : this.d_activeIndex === index;
},
getTabProp(tab, name) {
return tab.props ? tab.props[name] : undefined;
},
getKey(tab, index) {
return this.getTabProp(tab, 'header') || index;
},
getTabHeaderActionId(index) {
return `${this.id}_${index}_header_action`;
},
getTabContentId(index) {
return `${this.id}_${index}_content`;
},
onTabClick(event, tab, index) {
this.changeActiveIndex(event, tab, index);
this.$emit('tab-click', { originalEvent: event, index });
},
onTabKeyDown(event, tab, index) {
switch (event.code) {
case 'ArrowDown':
this.onTabArrowDownKey(event);
break;
case 'ArrowUp':
this.onTabArrowUpKey(event);
break;
case 'Home':
this.onTabHomeKey(event);
break;
case 'End':
this.onTabEndKey(event);
break;
case 'Enter':
case 'Space':
this.onTabEnterKey(event, tab, index);
break;
default:
break;
}
},
onTabArrowDownKey(event) {
const nextHeaderAction = this.findNextHeaderAction(event.target.parentElement.parentElement);
nextHeaderAction ? this.changeFocusedTab(event, nextHeaderAction) : this.onTabHomeKey(event);
event.preventDefault();
},
onTabArrowUpKey(event) {
const prevHeaderAction = this.findPrevHeaderAction(event.target.parentElement.parentElement);
prevHeaderAction ? this.changeFocusedTab(event, prevHeaderAction) : this.onTabEndKey(event);
event.preventDefault();
},
onTabHomeKey(event) {
const firstHeaderAction = this.findFirstHeaderAction();
this.changeFocusedTab(event, firstHeaderAction);
event.preventDefault();
},
onTabEndKey(event) {
const lastHeaderAction = this.findLastHeaderAction();
this.changeFocusedTab(event, lastHeaderAction);
event.preventDefault();
},
onTabEnterKey(event, tab, index) {
this.changeActiveIndex(event, tab, index);
event.preventDefault();
},
findNextHeaderAction(tabElement, selfCheck = false) {
const nextTabElement = selfCheck ? tabElement : tabElement.nextElementSibling;
const headerElement = DomHandler.findSingle(nextTabElement, '.p-accordion-header');
return headerElement ? (DomHandler.hasClass(headerElement, 'p-disabled') ? this.findNextHeaderAction(headerElement.parentElement) : DomHandler.findSingle(headerElement, '.p-accordion-header-action')) : null;
},
findPrevHeaderAction(tabElement, selfCheck = false) {
const prevTabElement = selfCheck ? tabElement : tabElement.previousElementSibling;
const headerElement = DomHandler.findSingle(prevTabElement, '.p-accordion-header');
return headerElement ? (DomHandler.hasClass(headerElement, 'p-disabled') ? this.findPrevHeaderAction(headerElement.parentElement) : DomHandler.findSingle(headerElement, '.p-accordion-header-action')) : null;
},
findFirstHeaderAction() {
return this.findNextHeaderAction(this.$el.firstElementChild, true);
},
findLastHeaderAction() {
return this.findPrevHeaderAction(this.$el.lastElementChild, true);
},
changeActiveIndex(event, tab, index) {
if (!this.getTabProp(tab, 'disabled')) {
const active = this.isTabActive(index);
const eventName = active ? 'tab-close' : 'tab-open'; const eventName = active ? 'tab-close' : 'tab-open';
if (this.multiple) { if (this.multiple) {
if (active) { if (active) {
this.d_activeIndex = this.d_activeIndex.filter(index => index !== i); this.d_activeIndex = this.d_activeIndex.filter((i) => i !== index);
} else {
if (this.d_activeIndex) this.d_activeIndex.push(index);
else this.d_activeIndex = [index];
} }
else { } else {
if (this.d_activeIndex) this.d_activeIndex = this.d_activeIndex === index ? null : index;
this.d_activeIndex.push(i);
else
this.d_activeIndex = [i];
}
}
else {
this.d_activeIndex = this.d_activeIndex === i ? null : i;
} }
this.$emit('update:activeIndex', this.d_activeIndex); this.$emit('update:activeIndex', this.d_activeIndex);
this.$emit(eventName, { originalEvent: event, index });
}
},
changeFocusedTab(event, element) {
if (element) {
DomHandler.focus(element);
this.$emit(eventName, { if (this.selectOnFocus) {
originalEvent: event, const index = parseInt(element.parentElement.parentElement.dataset.index, 10);
index: i const tab = this.tabs[index];
});
this.changeActiveIndex(event, tab, index);
} }
},
onTabKeydown(event, tab, i) {
if (event.which === 13) {
this.onTabClick(event, tab, i);
} }
}, },
isTabActive(index) {
if (this.multiple)
return this.d_activeIndex && this.d_activeIndex.includes(index);
else
return index === this.d_activeIndex;
},
getKey(tab, i) {
return (tab.props && tab.props.header) ? tab.props.header : i;
},
isTabDisabled(tab) {
return tab.props && tab.props.disabled;
},
getTabClass(i) { getTabClass(i) {
return ['p-accordion-tab', {'p-accordion-tab-active': this.isTabActive(i)}]; return [
'p-accordion-tab',
{
'p-accordion-tab-active': this.isTabActive(i)
}
];
}, },
getTabHeaderClass(tab, i) { getTabHeaderClass(tab, i) {
return ['p-accordion-header', {'p-highlight': this.isTabActive(i), 'p-disabled': this.isTabDisabled(tab)}]; return [
'p-accordion-header',
this.getTabProp(tab, 'headerClass'),
{
'p-highlight': this.isTabActive(i),
'p-disabled': this.getTabProp(tab, 'disabled')
}
];
}, },
getTabAriaId(i) { getTabHeaderIconClass(i) {
return this.ariaId + '_' + i; return ['p-accordion-toggle-icon pi', this.isTabActive(i) ? this.collapseIcon : this.expandIcon];
}, },
getHeaderCollapseIcon() { getTabContentClass(tab) {
return ['p-accordion-toggle-icon pi', this.collapseIcon]; return ['p-toggleable-content', this.getTabProp(tab, 'contentClass')];
},
getHeaderExpandIcon() {
return ['p-accordion-toggle-icon pi', this.expandIcon];
},
isAccordionTab(child) {
return child.type.name === 'AccordionTab';
} }
}, },
computed: { computed: {
tabs() { tabs() {
const tabs = [] return this.$slots.default().reduce((tabs, child) => {
this.$slots.default().forEach(child => {
if (this.isAccordionTab(child)) { if (this.isAccordionTab(child)) {
tabs.push(child); tabs.push(child);
} } else if (child.children && child.children instanceof Array) {
else if (child.children && child.children instanceof Array) { child.children.forEach((nestedChild) => {
child.children.forEach(nestedChild => {
if (this.isAccordionTab(nestedChild)) { if (this.isAccordionTab(nestedChild)) {
tabs.push(nestedChild) tabs.push(nestedChild);
} }
}); });
} }
}
)
return tabs; return tabs;
}, []);
}, },
ariaId() { id() {
return UniqueComponentId(); return this.$attrs.id || UniqueComponentId();
}
} }
},
directives: {
ripple: Ripple
} }
};
</script> </script>
<style> <style>
.p-accordion-header-link { .p-accordion-header-action {
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
@ -159,7 +276,7 @@ export default {
text-decoration: none; text-decoration: none;
} }
.p-accordion-header-link:focus { .p-accordion-header-action:focus {
z-index: 1; z-index: 1;
} }

View file

@ -1,4 +1,4 @@
import { VNode } from 'vue'; import { AnchorHTMLAttributes, HTMLAttributes, VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
export interface AccordionTabProps { export interface AccordionTabProps {
@ -6,6 +6,34 @@ export interface AccordionTabProps {
* Orientation of tab headers. * Orientation of tab headers.
*/ */
header?: string | undefined; header?: string | undefined;
/**
* Inline style of the tab header.
*/
headerStyle?: any;
/**
* Style class of the tab header.
*/
headerClass?: any;
/**
* Uses to pass all properties of the HTMLDivElement to the tab header.
*/
headerProps?: HTMLAttributes | undefined;
/**
* Uses to pass all properties of the HTMLAnchorElement to the focusable anchor element inside the tab header.
*/
headerActionProps?: AnchorHTMLAttributes | undefined;
/**
* Inline style of the tab content.
*/
contentStyle?: any;
/**
* Style class of the tab content.
*/
contentClass?: any;
/**
* Uses to pass all properties of the HTMLDivElement to the tab content.
*/
contentProps?: HTMLAttributes | undefined;
/** /**
* Whether the tab is disabled. * Whether the tab is disabled.
*/ */
@ -23,13 +51,13 @@ export interface AccordionTabSlots {
header: () => VNode[]; header: () => VNode[];
} }
export declare type AccordionTabEmits = { } export declare type AccordionTabEmits = {};
declare class AccordionTab extends ClassComponent<AccordionTabProps, AccordionTabSlots, AccordionTabEmits> {} declare class AccordionTab extends ClassComponent<AccordionTabProps, AccordionTabSlots, AccordionTabEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
AccordionTab: GlobalComponentConstructor<AccordionTab> AccordionTab: GlobalComponentConstructor<AccordionTab>;
} }
} }
@ -39,7 +67,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Accordion](https://www.primefaces.org/primevue/showcase/#/accordion) * - [Accordion](https://www.primefaces.org/primevue/accordion)
* *
*/ */
export default AccordionTab; export default AccordionTab;

View file

@ -7,7 +7,14 @@ export default {
name: 'AccordionTab', name: 'AccordionTab',
props: { props: {
header: null, header: null,
headerStyle: null,
headerClass: null,
headerProps: null,
headerActionProps: null,
contentStyle: null,
contentClass: null,
contentProps: null,
disabled: Boolean disabled: Boolean
} }
} };
</script> </script>

View file

@ -15,6 +15,6 @@ const FilterMatchMode = {
DATE_IS_NOT: 'dateIsNot', DATE_IS_NOT: 'dateIsNot',
DATE_BEFORE: 'dateBefore', DATE_BEFORE: 'dateBefore',
DATE_AFTER: 'dateAfter' DATE_AFTER: 'dateAfter'
} };
export default FilterMatchMode; export default FilterMatchMode;

View file

@ -1,6 +1,6 @@
const FilterOperator = { const FilterOperator = {
AND: 'and', AND: 'and',
OR: 'or' OR: 'or'
} };
export default FilterOperator; export default FilterOperator;

View file

@ -85,10 +85,8 @@ const FilterService = {
return false; return false;
} }
if (value.getTime && filter.getTime) if (value.getTime && filter.getTime) return value.getTime() === filter.getTime();
return value.getTime() === filter.getTime(); else return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) == ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
else
return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) == ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
}, },
notEquals(value, filter, filterLocale) { notEquals(value, filter, filterLocale) {
if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) { if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
@ -99,10 +97,8 @@ const FilterService = {
return true; return true;
} }
if (value.getTime && filter.getTime) if (value.getTime && filter.getTime) return value.getTime() !== filter.getTime();
return value.getTime() !== filter.getTime(); else return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) != ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
else
return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) != ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
}, },
in(value, filter) { in(value, filter) {
if (filter === undefined || filter === null || filter.length === 0) { if (filter === undefined || filter === null || filter.length === 0) {
@ -126,10 +122,8 @@ const FilterService = {
return false; return false;
} }
if (value.getTime) if (value.getTime) return filter[0].getTime() <= value.getTime() && value.getTime() <= filter[1].getTime();
return filter[0].getTime() <= value.getTime() && value.getTime() <= filter[1].getTime(); else return filter[0] <= value && value <= filter[1];
else
return filter[0] <= value && value <= filter[1];
}, },
lt(value, filter) { lt(value, filter) {
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
@ -140,10 +134,8 @@ const FilterService = {
return false; return false;
} }
if (value.getTime && filter.getTime) if (value.getTime && filter.getTime) return value.getTime() < filter.getTime();
return value.getTime() < filter.getTime(); else return value < filter;
else
return value < filter;
}, },
lte(value, filter) { lte(value, filter) {
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
@ -154,10 +146,8 @@ const FilterService = {
return false; return false;
} }
if (value.getTime && filter.getTime) if (value.getTime && filter.getTime) return value.getTime() <= filter.getTime();
return value.getTime() <= filter.getTime(); else return value <= filter;
else
return value <= filter;
}, },
gt(value, filter) { gt(value, filter) {
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
@ -168,10 +158,8 @@ const FilterService = {
return false; return false;
} }
if (value.getTime && filter.getTime) if (value.getTime && filter.getTime) return value.getTime() > filter.getTime();
return value.getTime() > filter.getTime(); else return value > filter;
else
return value > filter;
}, },
gte(value, filter) { gte(value, filter) {
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
@ -182,10 +170,8 @@ const FilterService = {
return false; return false;
} }
if (value.getTime && filter.getTime) if (value.getTime && filter.getTime) return value.getTime() >= filter.getTime();
return value.getTime() >= filter.getTime(); else return value >= filter;
else
return value >= filter;
}, },
dateIs(value, filter) { dateIs(value, filter) {
if (filter === undefined || filter === null) { if (filter === undefined || filter === null) {
@ -235,6 +221,6 @@ const FilterService = {
register(rule, fn) { register(rule, fn) {
this.filters[rule] = fn; this.filters[rule] = fn;
} }
} };
export default FilterService; export default FilterService;

View file

@ -235,6 +235,6 @@ const PrimeIcons = {
WINDOW_MAXIMIZE: 'pi pi-window-maximize', WINDOW_MAXIMIZE: 'pi pi-window-maximize',
WINDOW_MINIMIZE: 'pi pi-window-minimize', WINDOW_MINIMIZE: 'pi pi-window-minimize',
YOUTUBE: 'pi pi-youtube' YOUTUBE: 'pi pi-youtube'
} };
export default PrimeIcons; export default PrimeIcons;

View file

@ -202,6 +202,11 @@ export interface AutoCompleteProps {
* Default value is true. * Default value is true.
*/ */
autoOptionFocus?: boolean | undefined; autoOptionFocus?: boolean | undefined;
/**
* When enabled, the focused option is selected.
* Default value is false.
*/
selectOnFocus?: boolean | undefined;
/** /**
* Locale to use in searching. The default locale is the host environment's current locale. * Locale to use in searching. The default locale is the host environment's current locale.
*/ */
@ -233,11 +238,11 @@ export interface AutoCompleteProps {
/** /**
* Defines a string value that labels an interactive element. * Defines a string value that labels an interactive element.
*/ */
"aria-label"?: string | undefined; 'aria-label'?: string | undefined;
/** /**
* Identifier of the underlying input element. * Identifier of the underlying input element.
*/ */
"aria-labelledby"?: string | undefined; 'aria-labelledby'?: string | undefined;
} }
export interface AutoCompleteSlots { export interface AutoCompleteSlots {
@ -374,17 +379,17 @@ export declare type AutoCompleteEmits = {
* Callback to invoke on value change. * Callback to invoke on value change.
* @param {AutoCompleteChangeEvent} event - Custom change event. * @param {AutoCompleteChangeEvent} event - Custom change event.
*/ */
'change': (event: AutoCompleteChangeEvent) => void; change: (event: AutoCompleteChangeEvent) => void;
/** /**
* Callback to invoke when the component receives focus. * Callback to invoke when the component receives focus.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'focus': (event: Event) => void; focus: (event: Event) => void;
/** /**
* Callback to invoke when the component loses focus. * Callback to invoke when the component loses focus.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'blur': (event: Event) => void; blur: (event: Event) => void;
/** /**
* Callback to invoke when a suggestion is selected. * Callback to invoke when a suggestion is selected.
* @param {AutoCompleteItemSelectEvent} event - Custom item select event. * @param {AutoCompleteItemSelectEvent} event - Custom item select event.
@ -403,12 +408,12 @@ export declare type AutoCompleteEmits = {
/** /**
* Callback to invoke when input is cleared by the user. * Callback to invoke when input is cleared by the user.
*/ */
'clear': () => void; clear: () => void;
/** /**
* Callback to invoke to search for suggestions. * Callback to invoke to search for suggestions.
* @param {AutoCompleteCompleteEvent} event - Custom complete event. * @param {AutoCompleteCompleteEvent} event - Custom complete event.
*/ */
'complete': (event: AutoCompleteCompleteEvent) => void; complete: (event: AutoCompleteCompleteEvent) => void;
/** /**
* Callback to invoke before the overlay is shown. * Callback to invoke before the overlay is shown.
*/ */
@ -420,18 +425,18 @@ export declare type AutoCompleteEmits = {
/** /**
* Callback to invoke when the overlay is shown. * Callback to invoke when the overlay is shown.
*/ */
'show': () => void; show: () => void;
/** /**
* Callback to invoke when the overlay is hidden. * Callback to invoke when the overlay is hidden.
*/ */
'hide': () => void; hide: () => void;
} };
declare class AutoComplete extends ClassComponent<AutoCompleteProps, AutoCompleteSlots, AutoCompleteEmits> {} declare class AutoComplete extends ClassComponent<AutoCompleteProps, AutoCompleteSlots, AutoCompleteEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
AutoComplete: GlobalComponentConstructor<AutoComplete> AutoComplete: GlobalComponentConstructor<AutoComplete>;
} }
} }
@ -441,7 +446,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [AutoComplete](https://www.primefaces.org/primevue/showcase/#/autocomplete) * - [AutoComplete](https://www.primefaces.org/primevue/autocomplete)
* *
*/ */
export default AutoComplete; export default AutoComplete;

View file

@ -20,12 +20,12 @@ describe('AutoComplete.vue', () => {
data() { data() {
return { return {
countries: [ countries: [
{"name": "Afghanistan", "code": "AF"}, { name: 'Afghanistan', code: 'AF' },
{"name": "Bahrain", "code": "BH"}, { name: 'Bahrain', code: 'BH' },
{"name": "Chile", "code": "CL"}, { name: 'Chile', code: 'CL' },
{"name": "Denmark", "code": "DK"} { name: 'Denmark', code: 'DK' }
] ]
} };
} }
}); });
@ -38,7 +38,7 @@ describe('AutoComplete.vue', () => {
}); });
it('search copmlete', async () => { it('search copmlete', async () => {
const event = {'target': { 'value': 'b' }}; const event = { target: { value: 'b' } };
wrapper.vm.onInput(event); wrapper.vm.onInput(event);
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
@ -47,9 +47,7 @@ describe('AutoComplete.vue', () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
await wrapper.setProps({ await wrapper.setProps({
suggestions: [ suggestions: [{ name: 'Bahrain', code: 'BH' }]
{"name": "Bahrain", "code": "BH"}
]
}); });
expect(wrapper.find('.p-autocomplete-items').exists()).toBe(true); expect(wrapper.find('.p-autocomplete-items').exists()).toBe(true);

View file

@ -1,21 +1,86 @@
<template> <template>
<div ref="container" :class="containerClass" @click="onContainerClick"> <div ref="container" :class="containerClass" @click="onContainerClick">
<input v-if="!multiple" ref="focusInput" :id="inputId" type="text" :style="inputStyle" :class="inputStyleClass" :value="inputValue" :placeholder="placeholder" :tabindex="!disabled ? tabindex : -1" :disabled="disabled" autocomplete="off" <input
role="combobox" :aria-label="ariaLabel" :aria-labelledby="ariaLabelledby" aria-haspopup="listbox" aria-autocomplete="list" :aria-expanded="overlayVisible" :aria-controls="id + '_list'" :aria-activedescendant="focused ? focusedOptionId : undefined" v-if="!multiple"
@focus="onFocus" @blur="onBlur" @keydown="onKeyDown" @input="onInput" @change="onChange" v-bind="inputProps" /> ref="focusInput"
<ul v-if="multiple" ref="multiContainer" :class="multiContainerClass" tabindex="-1" role="listbox" aria-orientation="horizontal" :aria-activedescendant="focused ? focusedMultipleOptionId : undefined" :id="inputId"
@focus="onMultipleContainerFocus" @blur="onMultipleContainerBlur" @keydown="onMultipleContainerKeyDown"> type="text"
<li v-for="(option, i) of modelValue" :key="i" :id="id + '_multiple_option_' + i" :class="['p-autocomplete-token', {'p-focus': focusedMultipleOptionIndex === i}]" :style="inputStyle"
role="option" :aria-label="getOptionLabel(option)" :aria-selected="true" :aria-setsize="modelValue.length" :aria-posinset="i + 1"> :class="inputStyleClass"
:value="inputValue"
:placeholder="placeholder"
:tabindex="!disabled ? tabindex : -1"
:disabled="disabled"
autocomplete="off"
role="combobox"
:aria-label="ariaLabel"
:aria-labelledby="ariaLabelledby"
aria-haspopup="listbox"
aria-autocomplete="list"
:aria-expanded="overlayVisible"
:aria-controls="id + '_list'"
:aria-activedescendant="focused ? focusedOptionId : undefined"
@focus="onFocus"
@blur="onBlur"
@keydown="onKeyDown"
@input="onInput"
@change="onChange"
v-bind="inputProps"
/>
<ul
v-if="multiple"
ref="multiContainer"
:class="multiContainerClass"
tabindex="-1"
role="listbox"
aria-orientation="horizontal"
:aria-activedescendant="focused ? focusedMultipleOptionId : undefined"
@focus="onMultipleContainerFocus"
@blur="onMultipleContainerBlur"
@keydown="onMultipleContainerKeyDown"
>
<li
v-for="(option, i) of modelValue"
:key="i"
:id="id + '_multiple_option_' + i"
:class="['p-autocomplete-token', { 'p-focus': focusedMultipleOptionIndex === i }]"
role="option"
:aria-label="getOptionLabel(option)"
:aria-selected="true"
:aria-setsize="modelValue.length"
:aria-posinset="i + 1"
>
<slot name="chip" :value="option"> <slot name="chip" :value="option">
<span class="p-autocomplete-token-label">{{ getOptionLabel(option) }}</span> <span class="p-autocomplete-token-label">{{ getOptionLabel(option) }}</span>
</slot> </slot>
<span class="p-autocomplete-token-icon pi pi-times-circle" @click="removeOption($event, i)" aria-hidden="true"></span> <span class="p-autocomplete-token-icon pi pi-times-circle" @click="removeOption($event, i)" aria-hidden="true"></span>
</li> </li>
<li class="p-autocomplete-input-token" role="option"> <li class="p-autocomplete-input-token" role="option">
<input ref="focusInput" :id="inputId" type="text" :style="inputStyle" :class="inputClass" :placeholder="placeholder" :tabindex="!disabled ? tabindex : -1" :disabled="disabled" autocomplete="off" <input
role="combobox" :aria-label="ariaLabel" :aria-labelledby="ariaLabelledby" aria-haspopup="listbox" aria-autocomplete="list" :aria-expanded="overlayVisible" :aria-controls="id + '_list'" :aria-activedescendant="focused ? focusedOptionId : undefined" ref="focusInput"
@focus="onFocus" @blur="onBlur" @keydown="onKeyDown" @input="onInput" @change="onChange" v-bind="inputProps" /> :id="inputId"
type="text"
:style="inputStyle"
:class="inputClass"
:placeholder="placeholder"
:tabindex="!disabled ? tabindex : -1"
:disabled="disabled"
autocomplete="off"
role="combobox"
:aria-label="ariaLabel"
:aria-labelledby="ariaLabelledby"
aria-haspopup="listbox"
aria-autocomplete="list"
:aria-expanded="overlayVisible"
:aria-controls="id + '_list'"
:aria-activedescendant="focused ? focusedOptionId : undefined"
@focus="onFocus"
@blur="onBlur"
@keydown="onKeyDown"
@input="onInput"
@change="onChange"
v-bind="inputProps"
/>
</li> </li>
</ul> </ul>
<i v-if="searching" :class="loadingIconClass" aria-hidden="true"></i> <i v-if="searching" :class="loadingIconClass" aria-hidden="true"></i>
@ -27,19 +92,31 @@
<transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave"> <transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave">
<div v-if="overlayVisible" :ref="overlayRef" :class="panelStyleClass" :style="{ ...panelStyle, 'max-height': virtualScrollerDisabled ? scrollHeight : '' }" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="panelProps"> <div v-if="overlayVisible" :ref="overlayRef" :class="panelStyleClass" :style="{ ...panelStyle, 'max-height': virtualScrollerDisabled ? scrollHeight : '' }" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="panelProps">
<slot name="header" :value="modelValue" :suggestions="visibleOptions"></slot> <slot name="header" :value="modelValue" :suggestions="visibleOptions"></slot>
<VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :style="{'height': scrollHeight}" :items="visibleOptions" :tabindex="-1" :disabled="virtualScrollerDisabled"> <VirtualScroller :ref="virtualScrollerRef" v-bind="virtualScrollerOptions" :style="{ height: scrollHeight }" :items="visibleOptions" :tabindex="-1" :disabled="virtualScrollerDisabled">
<template v-slot:content="{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }"> <template v-slot:content="{ styleClass, contentRef, items, getItemOptions, contentStyle, itemSize }">
<ul :ref="(el) => listRef(el, contentRef)" :id="id + '_list'" :class="['p-autocomplete-items', styleClass]" :style="contentStyle" role="listbox"> <ul :ref="(el) => listRef(el, contentRef)" :id="id + '_list'" :class="['p-autocomplete-items', styleClass]" :style="contentStyle" role="listbox">
<template v-for="(option, i) of items" :key="getOptionRenderKey(option, getOptionIndex(i, getItemOptions))"> <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="p-autocomplete-item-group" role="option"> <li v-if="isOptionGroup(option)" :id="id + '_' + getOptionIndex(i, getItemOptions)" :style="{ height: itemSize ? itemSize + 'px' : undefined }" class="p-autocomplete-item-group" role="option">
<slot name="optiongroup" :option="option.optionGroup" :item="option.optionGroup" :index="getOptionIndex(i, getItemOptions)">{{ getOptionGroupLabel(option.optionGroup) }}</slot> <slot name="optiongroup" :option="option.optionGroup" :item="option.optionGroup" :index="getOptionIndex(i, getItemOptions)">{{ getOptionGroupLabel(option.optionGroup) }}</slot>
</li> </li>
<li v-else v-ripple :id="id + '_' + getOptionIndex(i, getItemOptions)" :style="{height: itemSize ? itemSize + 'px' : undefined}" <li
v-else
:id="id + '_' + getOptionIndex(i, getItemOptions)"
v-ripple
:style="{ height: itemSize ? itemSize + 'px' : undefined }"
:class="['p-autocomplete-item', { 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex === getOptionIndex(i, getItemOptions), 'p-disabled': isOptionDisabled(option) }]" :class="['p-autocomplete-item', { 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex === getOptionIndex(i, getItemOptions), 'p-disabled': isOptionDisabled(option) }]"
role="option" :aria-label="getOptionLabel(option)" :aria-selected="isSelected(option)" :aria-disabled="isOptionDisabled(option)" :aria-setsize="ariaSetSize" :aria-posinset="getAriaPosInset(getOptionIndex(i, getItemOptions))" role="option"
@click="onOptionSelect($event, option)" @mousemove="onOptionMouseMove($event, getOptionIndex(i, getItemOptions))"> :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))"
>
<slot v-if="$slots.option" name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">{{ getOptionLabel(option) }}</slot> <slot v-if="$slots.option" name="option" :option="option" :index="getOptionIndex(i, getItemOptions)">{{ getOptionLabel(option) }}</slot>
<slot v-else name="item" :item="option" :index="getOptionIndex(i, getItemOptions)">{{getOptionLabel(option)}}</slot> <!--TODO: Deprecated since v3.16.0--> <slot v-else name="item" :item="option" :index="getOptionIndex(i, getItemOptions)">{{ getOptionLabel(option) }}</slot>
<!--TODO: Deprecated since v3.16.0-->
</li> </li>
</template> </template>
</ul> </ul>
@ -75,7 +152,8 @@ export default {
type: Array, type: Array,
default: null default: null
}, },
field: { // TODO: Deprecated since v3.16.0 field: {
// TODO: Deprecated since v3.16.0
type: [String, Function], type: [String, Function],
default: null default: null
}, },
@ -95,7 +173,8 @@ export default {
type: String, type: String,
default: 'blank' default: 'blank'
}, },
autoHighlight: { // TODO: Deprecated since v3.16.0 autoHighlight: {
// TODO: Deprecated since v3.16.0. Use selectOnFocus property instead.
type: Boolean, type: Boolean,
default: false default: false
}, },
@ -135,13 +214,34 @@ export default {
type: Boolean, type: Boolean,
default: false default: false
}, },
inputId: String, inputId: {
inputStyle: null, type: String,
inputClass: null, default: null
inputProps: null, },
panelStyle: null, inputStyle: {
panelClass: null, type: null,
panelProps: null, default: null
},
inputClass: {
type: String,
default: null
},
inputProps: {
type: null,
default: null
},
panelStyle: {
type: null,
default: null
},
panelClass: {
type: String,
default: null
},
panelProps: {
type: null,
default: null
},
loadingIcon: { loadingIcon: {
type: String, type: String,
default: 'pi pi-spinner' default: 'pi pi-spinner'
@ -154,6 +254,10 @@ export default {
type: Boolean, type: Boolean,
default: true default: true
}, },
selectOnFocus: {
type: Boolean,
default: false
},
searchLocale: { searchLocale: {
type: String, type: String,
default: undefined default: undefined
@ -193,18 +297,16 @@ export default {
overlay: null, overlay: null,
virtualScroller: null, virtualScroller: null,
searchTimeout: null, searchTimeout: null,
selectOnFocus: false,
focusOnHover: false, focusOnHover: false,
dirty: false, dirty: false,
data() { data() {
return { return {
id: UniqueComponentId(),
focused: false, focused: false,
focusedOptionIndex: -1, focusedOptionIndex: -1,
focusedMultipleOptionIndex: -1, focusedMultipleOptionIndex: -1,
overlayVisible: false, overlayVisible: false,
searching: false searching: false
} };
}, },
watch: { watch: {
suggestions() { suggestions() {
@ -218,8 +320,6 @@ export default {
} }
}, },
mounted() { mounted() {
this.id = this.$attrs.id || this.id;
this.autoUpdateModel(); this.autoUpdateModel();
}, },
updated() { updated() {
@ -243,7 +343,7 @@ export default {
}, },
methods: { methods: {
getOptionIndex(index, fn) { getOptionIndex(index, fn) {
return this.virtualScrollerDisabled ? index : (fn && fn(index)['index']); return this.virtualScrollerDisabled ? index : fn && fn(index)['index'];
}, },
getOptionLabel(option) { getOptionLabel(option) {
return this.field || this.optionLabel ? ObjectUtils.resolveFieldData(option, this.field || this.optionLabel) : option; return this.field || this.optionLabel ? ObjectUtils.resolveFieldData(option, this.field || this.optionLabel) : option;
@ -267,15 +367,15 @@ export default {
return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren); return ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren);
}, },
getAriaPosInset(index) { getAriaPosInset(index) {
return (this.optionGroupLabel ? index - this.visibleOptions.slice(0, index).filter(option => this.isOptionGroup(option)).length : index) + 1; return (this.optionGroupLabel ? index - this.visibleOptions.slice(0, index).filter((option) => this.isOptionGroup(option)).length : index) + 1;
}, },
show(isFocus) { show(isFocus) {
this.$emit('before-show'); this.$emit('before-show');
this.dirty = true; this.dirty = true;
this.overlayVisible = true; this.overlayVisible = true;
this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : (this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1); this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
isFocus && this.$refs.focusInput.focus(); isFocus && DomHandler.focus(this.$refs.focusInput);
}, },
hide(isFocus) { hide(isFocus) {
const _hide = () => { const _hide = () => {
@ -284,10 +384,12 @@ export default {
this.overlayVisible = false; this.overlayVisible = false;
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
isFocus && this.$refs.focusInput.focus(); isFocus && DomHandler.focus(this.$refs.focusInput);
} };
setTimeout(() => { _hide() }, 0); // For ScreenReaders setTimeout(() => {
_hide();
}, 0); // For ScreenReaders
}, },
onFocus(event) { onFocus(event) {
if (!this.dirty && this.completeOnFocus) { if (!this.dirty && this.completeOnFocus) {
@ -371,6 +473,7 @@ export default {
} }
let query = event.target.value; let query = event.target.value;
if (!this.multiple) { if (!this.multiple) {
this.updateModel(event, query); this.updateModel(event, query);
} }
@ -378,16 +481,14 @@ export default {
if (query.length === 0) { if (query.length === 0) {
this.hide(); this.hide();
this.$emit('clear'); this.$emit('clear');
} } else {
else {
if (query.length >= this.minLength) { if (query.length >= this.minLength) {
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
this.searchTimeout = setTimeout(() => { this.searchTimeout = setTimeout(() => {
this.search(event, query, 'input'); this.search(event, query, 'input');
}, this.delay); }, this.delay);
} } else {
else {
this.hide(); this.hide();
} }
} }
@ -397,7 +498,7 @@ export default {
let valid = false; let valid = false;
if (this.visibleOptions) { if (this.visibleOptions) {
const matchedValue = this.visibleOptions.find(option => this.isOptionMatched(option, event.target.value)); const matchedValue = this.visibleOptions.find((option) => this.isOptionMatched(option, event.target.value));
if (matchedValue !== undefined) { if (matchedValue !== undefined) {
valid = true; valid = true;
@ -443,7 +544,7 @@ export default {
} }
if (!this.overlay || !this.overlay.contains(event.target)) { if (!this.overlay || !this.overlay.contains(event.target)) {
this.$refs.focusInput.focus(); DomHandler.focus(this.$refs.focusInput);
} }
}, },
onDropdownClick(event) { onDropdownClick(event) {
@ -451,20 +552,17 @@ export default {
if (this.overlayVisible) { if (this.overlayVisible) {
this.hide(true); this.hide(true);
} } else {
else { DomHandler.focus(this.$refs.focusInput);
this.$refs.focusInput.focus();
query = this.$refs.focusInput.value; query = this.$refs.focusInput.value;
if (this.dropdownMode === 'blank') if (this.dropdownMode === 'blank') this.search(event, '', 'dropdown');
this.search(event, '', 'dropdown'); else if (this.dropdownMode === 'current') this.search(event, query, 'dropdown');
else if (this.dropdownMode === 'current')
this.search(event, query, 'dropdown');
} }
this.$emit('dropdown-click', { originalEvent: event, query }); this.$emit('dropdown-click', { originalEvent: event, query });
}, },
onOptionSelect(event, option) { onOptionSelect(event, option, isHide = true) {
const value = this.getOptionValue(option); const value = this.getOptionValue(option);
if (this.multiple) { if (this.multiple) {
@ -473,14 +571,13 @@ export default {
if (!this.isSelected(option)) { if (!this.isSelected(option)) {
this.updateModel(event, [...(this.modelValue || []), value]); this.updateModel(event, [...(this.modelValue || []), value]);
} }
} } else {
else {
this.updateModel(event, value); this.updateModel(event, value);
} }
this.$emit('item-select', { originalEvent: event, value: option }); this.$emit('item-select', { originalEvent: event, value: option });
this.hide(true); isHide && this.hide(true);
}, },
onOptionMouseMove(event, index) { onOptionMouseMove(event, index) {
if (this.focusOnHover) { if (this.focusOnHover) {
@ -526,8 +623,7 @@ export default {
this.overlayVisible && this.hide(); this.overlayVisible && this.hide();
event.preventDefault(); event.preventDefault();
} } else {
else {
const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.findLastFocusedOptionIndex(); const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.findLastFocusedOptionIndex();
this.changeFocusedOptionIndex(event, optionIndex); this.changeFocusedOptionIndex(event, optionIndex);
@ -537,14 +633,14 @@ export default {
}, },
onArrowLeftKey(event) { onArrowLeftKey(event) {
const target = event.currentTarget; const target = event.currentTarget;
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
if (this.multiple) { if (this.multiple) {
if (ObjectUtils.isEmpty(target.value) && this.hasSelectedOption) { if (ObjectUtils.isEmpty(target.value) && this.hasSelectedOption) {
this.$refs.multiContainer.focus(); DomHandler.focus(this.$refs.multiContainer);
this.focusedMultipleOptionIndex = this.modelValue.length; this.focusedMultipleOptionIndex = this.modelValue.length;
} } else {
else {
event.stopPropagation(); // To prevent onArrowLeftKeyOnMultiple method event.stopPropagation(); // To prevent onArrowLeftKeyOnMultiple method
} }
} }
@ -563,6 +659,7 @@ export default {
onEndKey(event) { onEndKey(event) {
const target = event.currentTarget; const target = event.currentTarget;
const len = target.value.length; const len = target.value.length;
target.setSelectionRange(len, len); target.setSelectionRange(len, len);
this.focusedOptionIndex = -1; this.focusedOptionIndex = -1;
@ -579,8 +676,7 @@ export default {
onEnterKey(event) { onEnterKey(event) {
if (!this.overlayVisible) { if (!this.overlayVisible) {
this.onArrowDownKey(event); this.onArrowDownKey(event);
} } else {
else {
if (this.focusedOptionIndex !== -1) { if (this.focusedOptionIndex !== -1) {
this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]); this.onOptionSelect(event, this.visibleOptions[this.focusedOptionIndex]);
} }
@ -620,9 +716,9 @@ export default {
onArrowRightKeyOnMultiple() { onArrowRightKeyOnMultiple() {
this.focusedMultipleOptionIndex++; this.focusedMultipleOptionIndex++;
if (this.focusedMultipleOptionIndex > (this.modelValue.length - 1)) { if (this.focusedMultipleOptionIndex > this.modelValue.length - 1) {
this.focusedMultipleOptionIndex = -1; this.focusedMultipleOptionIndex = -1;
this.$refs.focusInput.focus(); DomHandler.focus(this.$refs.focusInput);
} }
}, },
onBackspaceKeyOnMultiple(event) { onBackspaceKeyOnMultiple(event) {
@ -654,10 +750,10 @@ export default {
}, },
alignOverlay() { alignOverlay() {
let target = this.multiple ? this.$refs.multiContainer : this.$refs.focusInput; let target = this.multiple ? this.$refs.multiContainer : this.$refs.focusInput;
if (this.appendTo === 'self') { if (this.appendTo === 'self') {
DomHandler.relativePosition(this.overlay, target); DomHandler.relativePosition(this.overlay, target);
} } else {
else {
this.overlay.style.minWidth = DomHandler.getOuterWidth(target) + 'px'; this.overlay.style.minWidth = DomHandler.getOuterWidth(target) + 'px';
DomHandler.absolutePosition(this.overlay, target); DomHandler.absolutePosition(this.overlay, target);
} }
@ -669,6 +765,7 @@ export default {
this.hide(); this.hide();
} }
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
@ -701,6 +798,7 @@ export default {
this.hide(); this.hide();
} }
}; };
window.addEventListener('resize', this.resizeListener); window.addEventListener('resize', this.resizeListener);
} }
}, },
@ -714,13 +812,11 @@ export default {
return !this.overlay.contains(event.target) && !this.isInputClicked(event) && !this.isDropdownClicked(event); return !this.overlay.contains(event.target) && !this.isInputClicked(event) && !this.isDropdownClicked(event);
}, },
isInputClicked(event) { isInputClicked(event) {
if (this.multiple) if (this.multiple) return event.target === this.$refs.multiContainer || this.$refs.multiContainer.contains(event.target);
return event.target === this.$refs.multiContainer || this.$refs.multiContainer.contains(event.target); else return event.target === this.$refs.focusInput;
else
return event.target === this.$refs.focusInput;
}, },
isDropdownClicked(event) { isDropdownClicked(event) {
return this.$refs.dropdownButton ? (event.target === this.$refs.dropdownButton || this.$refs.dropdownButton.$el.contains(event.target)) : false; return this.$refs.dropdownButton ? event.target === this.$refs.dropdownButton || this.$refs.dropdownButton.$el.contains(event.target) : false;
}, },
isOptionMatched(option, value) { isOptionMatched(option, value) {
return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.searchLocale) === value.toLocaleLowerCase(this.searchLocale); return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.searchLocale) === value.toLocaleLowerCase(this.searchLocale);
@ -735,28 +831,32 @@ export default {
return ObjectUtils.equals(this.modelValue, this.getOptionValue(option), this.equalityKey); return ObjectUtils.equals(this.modelValue, this.getOptionValue(option), this.equalityKey);
}, },
findFirstOptionIndex() { findFirstOptionIndex() {
return this.visibleOptions.findIndex(option => this.isValidOption(option)); return this.visibleOptions.findIndex((option) => this.isValidOption(option));
}, },
findLastOptionIndex() { findLastOptionIndex() {
return ObjectUtils.findLastIndex(this.visibleOptions, option => this.isValidOption(option)); return ObjectUtils.findLastIndex(this.visibleOptions, (option) => this.isValidOption(option));
}, },
findNextOptionIndex(index) { findNextOptionIndex(index) {
const matchedOptionIndex = index < (this.visibleOptions.length - 1) ? this.visibleOptions.slice(index + 1).findIndex(option => this.isValidOption(option)) : -1; 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; return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
}, },
findPrevOptionIndex(index) { findPrevOptionIndex(index) {
const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions.slice(0, index), option => this.isValidOption(option)) : -1; const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions.slice(0, index), (option) => this.isValidOption(option)) : -1;
return matchedOptionIndex > -1 ? matchedOptionIndex : index; return matchedOptionIndex > -1 ? matchedOptionIndex : index;
}, },
findSelectedOptionIndex() { findSelectedOptionIndex() {
return this.hasSelectedOption ? this.visibleOptions.findIndex(option => this.isValidSelectedOption(option)) : -1; return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
}, },
findFirstFocusedOptionIndex() { findFirstFocusedOptionIndex() {
const selectedIndex = this.findSelectedOptionIndex(); const selectedIndex = this.findSelectedOptionIndex();
return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex; return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
}, },
findLastFocusedOptionIndex() { findLastFocusedOptionIndex() {
const selectedIndex = this.findSelectedOptionIndex(); const selectedIndex = this.findSelectedOptionIndex();
return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex; return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
}, },
search(event, query, source) { search(event, query, source) {
@ -775,12 +875,12 @@ export default {
}, },
removeOption(event, index) { removeOption(event, index) {
const removedOption = this.modelValue[index]; const removedOption = this.modelValue[index];
const value = this.modelValue.filter((_, i) => i !== index).map(option => this.getOptionValue(option)); const value = this.modelValue.filter((_, i) => i !== index).map((option) => this.getOptionValue(option));
this.updateModel(event, value); this.updateModel(event, value);
this.$emit('item-unselect', { originalEvent: event, value: removedOption }); this.$emit('item-unselect', { originalEvent: event, value: removedOption });
this.dirty = true; this.dirty = true;
this.$refs.focusInput.focus(); DomHandler.focus(this.$refs.focusInput);
}, },
changeFocusedOptionIndex(event, index) { changeFocusedOptionIndex(event, index) {
if (this.focusedOptionIndex !== index) { if (this.focusedOptionIndex !== index) {
@ -788,17 +888,17 @@ export default {
this.scrollInView(); this.scrollInView();
if (this.selectOnFocus || this.autoHighlight) { if (this.selectOnFocus || this.autoHighlight) {
this.updateModel(event, this.getOptionValue(this.visibleOptions[index])); this.onOptionSelect(event, this.visibleOptions[index], false);
} }
} }
}, },
scrollInView(index = -1) { scrollInView(index = -1) {
const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId; const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
const element = DomHandler.findSingle(this.list, `li[id="${id}"]`); const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
if (element) { if (element) {
element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' }); element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
} } else if (!this.virtualScrollerDisabled) {
else if (!this.virtualScrollerDisabled) {
setTimeout(() => { setTimeout(() => {
this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex); this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
}, 0); }, 0);
@ -807,8 +907,7 @@ export default {
autoUpdateModel() { autoUpdateModel() {
if ((this.selectOnFocus || this.autoHighlight) && this.autoOptionFocus && !this.hasSelectedOption) { if ((this.selectOnFocus || this.autoHighlight) && this.autoOptionFocus && !this.hasSelectedOption) {
this.focusedOptionIndex = this.findFirstFocusedOptionIndex(); this.focusedOptionIndex = this.findFirstFocusedOptionIndex();
const value = this.getOptionValue(this.visibleOptions[this.focusedOptionIndex]); this.onOptionSelect(null, this.visibleOptions[this.focusedOptionIndex], false);
this.updateModel(null, this.multiple ? [value] : value);
} }
}, },
updateModel(event, value) { updateModel(event, value) {
@ -820,7 +919,8 @@ export default {
result.push({ optionGroup: option, group: true, index }); result.push({ optionGroup: option, group: true, index });
const optionGroupChildren = this.getOptionGroupChildren(option); const optionGroupChildren = this.getOptionGroupChildren(option);
optionGroupChildren && optionGroupChildren.forEach(o => result.push(o));
optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o));
return result; return result;
}, []); }, []);
@ -838,7 +938,9 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-autocomplete p-component p-inputwrapper', { return [
'p-autocomplete p-component p-inputwrapper',
{
'p-disabled': this.disabled, 'p-disabled': this.disabled,
'p-focus': this.focused, 'p-focus': this.focused,
'p-autocomplete-dd': this.dropdown, 'p-autocomplete-dd': this.dropdown,
@ -846,39 +948,47 @@ export default {
'p-inputwrapper-filled': this.modelValue || ObjectUtils.isNotEmpty(this.inputValue), 'p-inputwrapper-filled': this.modelValue || ObjectUtils.isNotEmpty(this.inputValue),
'p-inputwrapper-focus': this.focused, 'p-inputwrapper-focus': this.focused,
'p-overlay-open': this.overlayVisible 'p-overlay-open': this.overlayVisible
}]; }
];
}, },
inputStyleClass() { inputStyleClass() {
return ['p-autocomplete-input p-inputtext p-component', this.inputClass, { return [
'p-autocomplete-input p-inputtext p-component',
this.inputClass,
{
'p-autocomplete-dd-input': this.dropdown 'p-autocomplete-dd-input': this.dropdown
}]; }
];
}, },
multiContainerClass() { multiContainerClass() {
return ['p-autocomplete-multiple-container p-component p-inputtext']; return ['p-autocomplete-multiple-container p-component p-inputtext'];
}, },
panelStyleClass() { panelStyleClass() {
return ['p-autocomplete-panel p-component', this.panelClass, { return [
'p-autocomplete-panel p-component',
this.panelClass,
{
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}]; }
];
}, },
loadingIconClass() { loadingIconClass() {
return ['p-autocomplete-loader pi-spin', this.loadingIcon]; return ['p-autocomplete-loader pi-spin', this.loadingIcon];
}, },
visibleOptions() { visibleOptions() {
return this.optionGroupLabel ? this.flatOptions(this.suggestions) : (this.suggestions || []); return this.optionGroupLabel ? this.flatOptions(this.suggestions) : this.suggestions || [];
}, },
inputValue() { inputValue() {
if (this.modelValue) { if (this.modelValue) {
if (typeof this.modelValue === 'object') { if (typeof this.modelValue === 'object') {
const label = this.getOptionLabel(this.modelValue); const label = this.getOptionLabel(this.modelValue);
return label != null ? label : this.modelValue; return label != null ? label : this.modelValue;
} } else {
else {
return this.modelValue; return this.modelValue;
} }
} } else {
else {
return ''; return '';
} }
}, },
@ -906,6 +1016,9 @@ export default {
selectedMessageText() { selectedMessageText() {
return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', this.multiple ? this.modelValue.length : '1') : this.emptySelectionMessageText; return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', this.multiple ? this.modelValue.length : '1') : this.emptySelectionMessageText;
}, },
id() {
return this.$attrs.id || UniqueComponentId();
},
focusedOptionId() { focusedOptionId() {
return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null; return this.focusedOptionIndex !== -1 ? `${this.id}_${this.focusedOptionIndex}` : null;
}, },
@ -913,21 +1026,21 @@ export default {
return this.focusedMultipleOptionIndex !== -1 ? `${this.id}_multiple_option_${this.focusedMultipleOptionIndex}` : null; return this.focusedMultipleOptionIndex !== -1 ? `${this.id}_multiple_option_${this.focusedMultipleOptionIndex}` : null;
}, },
ariaSetSize() { ariaSetSize() {
return this.visibleOptions.filter(option => !this.isOptionGroup(option)).length; return this.visibleOptions.filter((option) => !this.isOptionGroup(option)).length;
}, },
virtualScrollerDisabled() { virtualScrollerDisabled() {
return !this.virtualScrollerOptions; return !this.virtualScrollerOptions;
} }
}, },
components: { components: {
'Button': Button, Button: Button,
'VirtualScroller': VirtualScroller, VirtualScroller: VirtualScroller,
'Portal': Portal Portal: Portal
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}
} }
};
</script> </script>
<style> <style>
@ -939,7 +1052,7 @@ export default {
.p-autocomplete-loader { .p-autocomplete-loader {
position: absolute; position: absolute;
top: 50%; top: 50%;
margin-top: -.5rem; margin-top: -0.5rem;
} }
.p-autocomplete-dd .p-autocomplete-input { .p-autocomplete-dd .p-autocomplete-input {

View file

@ -44,13 +44,13 @@ export declare type AvatarEmits = {
* Triggered when an error occurs while loading an image file. * Triggered when an error occurs while loading an image file.
*/ */
error: () => void; error: () => void;
} };
declare class Avatar extends ClassComponent<AvatarProps, AvatarSlots, AvatarEmits> {} declare class Avatar extends ClassComponent<AvatarProps, AvatarSlots, AvatarEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Avatar: GlobalComponentConstructor<Avatar> Avatar: GlobalComponentConstructor<Avatar>;
} }
} }
@ -60,7 +60,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Avatar](https://www.primefaces.org/primevue/showcase/#/avatar) * - [Avatar](https://www.primefaces.org/primevue/avatar)
* *
*/ */
export default Avatar; export default Avatar;

View file

@ -1,9 +1,9 @@
<template> <template>
<div :class="containerClass"> <div :class="containerClass">
<slot> <slot>
<span class="p-avatar-text" v-if="label">{{label}}</span> <span v-if="label" class="p-avatar-text">{{ label }}</span>
<span :class="iconClass" v-else-if="icon"></span> <span v-else-if="icon" :class="iconClass"></span>
<img :src="image" v-else-if="image" @error="onError"> <img v-else-if="image" :src="image" @error="onError" />
</slot> </slot>
</div> </div>
</template> </template>
@ -11,6 +11,7 @@
<script> <script>
export default { export default {
name: 'Avatar', name: 'Avatar',
emits: ['error'],
props: { props: {
label: { label: {
type: String, type: String,
@ -30,7 +31,7 @@ export default {
}, },
shape: { shape: {
type: String, type: String,
default: "square" default: 'square'
} }
}, },
methods: { methods: {
@ -40,18 +41,21 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-avatar p-component', { return [
'p-avatar p-component',
{
'p-avatar-image': this.image != null, 'p-avatar-image': this.image != null,
'p-avatar-circle': this.shape === 'circle', 'p-avatar-circle': this.shape === 'circle',
'p-avatar-lg': this.size === 'large', 'p-avatar-lg': this.size === 'large',
'p-avatar-xl': this.size === 'xlarge' 'p-avatar-xl': this.size === 'xlarge'
}]; }
];
}, },
iconClass() { iconClass() {
return ['p-avatar-icon', this.icon]; return ['p-avatar-icon', this.icon];
} }
} }
} };
</script> </script>
<style> <style>

View file

@ -1,19 +1,16 @@
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
export interface AvatarGroupProps { export interface AvatarGroupProps {}
}
export interface AvatarGroupSlots { export interface AvatarGroupSlots {}
}
export declare type AvatarGroupEmits = { export declare type AvatarGroupEmits = {};
}
declare class AvatarGroup extends ClassComponent<AvatarGroupProps, AvatarGroupSlots, AvatarGroupEmits> {} declare class AvatarGroup extends ClassComponent<AvatarGroupProps, AvatarGroupSlots, AvatarGroupEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
AvatarGroup: GlobalComponentConstructor<AvatarGroup> AvatarGroup: GlobalComponentConstructor<AvatarGroup>;
} }
} }
@ -27,7 +24,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [AvatarGroup](https://www.primefaces.org/primevue/showcase/#/avatar) * - [AvatarGroup](https://www.primefaces.org/primevue/avatar)
* *
*/ */
export default AvatarGroup; export default AvatarGroup;

View file

@ -7,7 +7,7 @@
<script> <script>
export default { export default {
name: 'AvatarGroup' name: 'AvatarGroup'
} };
</script> </script>
<style> <style>

View file

@ -29,14 +29,13 @@ export interface BadgeSlots {
default: () => VNode[]; default: () => VNode[];
} }
export declare type BadgeEmits = { export declare type BadgeEmits = {};
}
declare class Badge extends ClassComponent<BadgeProps, BadgeSlots, BadgeEmits> {} declare class Badge extends ClassComponent<BadgeProps, BadgeSlots, BadgeEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Badge: GlobalComponentConstructor<Badge> Badge: GlobalComponentConstructor<Badge>;
} }
} }
@ -46,7 +45,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Badge](https://www.primefaces.org/primevue/showcase/#/badge) * - [Badge](https://www.primefaces.org/primevue/badge)
* *
*/ */
export default Badge; export default Badge;

View file

@ -17,7 +17,9 @@ export default {
return this.$slots.default ? 'p-overlay-badge' : this.badgeClass; return this.$slots.default ? 'p-overlay-badge' : this.badgeClass;
}, },
badgeClass() { badgeClass() {
return ['p-badge p-component', { return [
'p-badge p-component',
{
'p-badge-no-gutter': this.value && String(this.value).length === 1, 'p-badge-no-gutter': this.value && String(this.value).length === 1,
'p-badge-dot': !this.value && !this.$slots.default, 'p-badge-dot': !this.value && !this.$slots.default,
'p-badge-lg': this.size === 'large', 'p-badge-lg': this.size === 'large',
@ -26,8 +28,9 @@ export default {
'p-badge-success': this.severity === 'success', 'p-badge-success': this.severity === 'success',
'p-badge-warning': this.severity === 'warning', 'p-badge-warning': this.severity === 'warning',
'p-badge-danger': this.severity === 'danger' 'p-badge-danger': this.severity === 'danger'
}]; }
} ];
} }
} }
};
</script> </script>

View file

@ -4,9 +4,11 @@ import {UniqueComponentId} from 'primevue/utils';
const BadgeDirective = { const BadgeDirective = {
beforeMount(el, options) { beforeMount(el, options) {
const id = UniqueComponentId() + '_badge'; const id = UniqueComponentId() + '_badge';
el.$_pbadgeId = id; el.$_pbadgeId = id;
let badge = document.createElement('span'); let badge = document.createElement('span');
badge.id = id; badge.id = id;
badge.className = 'p-badge p-component'; badge.className = 'p-badge p-component';
@ -20,8 +22,7 @@ const BadgeDirective = {
if (String(options.value).length === 1) { if (String(options.value).length === 1) {
DomHandler.addClass(badge, 'p-badge-no-gutter'); DomHandler.addClass(badge, 'p-badge-no-gutter');
} }
} } else {
else {
DomHandler.addClass(badge, 'p-badge-dot'); DomHandler.addClass(badge, 'p-badge-dot');
} }
@ -39,12 +40,9 @@ const BadgeDirective = {
DomHandler.removeClass(badge, 'p-badge-dot'); DomHandler.removeClass(badge, 'p-badge-dot');
} }
if (String(options.value).length === 1) if (String(options.value).length === 1) DomHandler.addClass(badge, 'p-badge-no-gutter');
DomHandler.addClass(badge, 'p-badge-no-gutter'); else DomHandler.removeClass(badge, 'p-badge-no-gutter');
else } else if (!options.value && !DomHandler.hasClass(badge, 'p-badge-dot')) {
DomHandler.removeClass(badge, 'p-badge-no-gutter');
}
else if (!options.value && !DomHandler.hasClass(badge, 'p-badge-dot')) {
DomHandler.addClass(badge, 'p-badge-dot'); DomHandler.addClass(badge, 'p-badge-dot');
} }

View file

@ -6,7 +6,7 @@ describe('directive badge should exist', () => {
const wrapper = mount({ const wrapper = mount({
template: '<i class="pi pi-bell mr-4 p-text-secondary" style="font-size: 2rem" v-badge="2"></i>', template: '<i class="pi pi-bell mr-4 p-text-secondary" style="font-size: 2rem" v-badge="2"></i>',
directives: { directives: {
'badge': BadgeDirective badge: BadgeDirective
} }
}); });

View file

@ -33,18 +33,18 @@ export declare type BlockUIEmits = {
/** /**
* Fired when the element gets blocked. * Fired when the element gets blocked.
*/ */
'block': () => void; block: () => void;
/** /**
* Fired when the element gets unblocked. * Fired when the element gets unblocked.
*/ */
'unblock': () => void; unblock: () => void;
} };
declare class BlockUI extends ClassComponent<BlockUIProps, BlockUISlots, BlockUIEmits> {} declare class BlockUI extends ClassComponent<BlockUIProps, BlockUISlots, BlockUIEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
BlockUI: GlobalComponentConstructor<BlockUI> BlockUI: GlobalComponentConstructor<BlockUI>;
} }
} }
@ -54,7 +54,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [BlockUI](https://www.primefaces.org/primevue/showcase/#/blockui) * - [BlockUI](https://www.primefaces.org/primevue/blockui)
* *
*/ */
export default BlockUI; export default BlockUI;

View file

@ -11,7 +11,7 @@ config.global.mocks = {
} }
} }
} }
} };
describe('BlockUI.vue', () => { describe('BlockUI.vue', () => {
it('should blocked and unblocked the panel', async () => { it('should blocked and unblocked the panel', async () => {
@ -34,7 +34,7 @@ describe('BlockUI.vue', () => {
data() { data() {
return { return {
blockedPanel: false blockedPanel: false
} };
}, },
methods: { methods: {
blockPanel() { blockPanel() {

View file

@ -29,22 +29,21 @@ export default {
} }
}, },
mask: null, mask: null,
watch: {
blocked(newValue) {
if (newValue === true) this.block();
else this.unblock();
}
},
mounted() { mounted() {
if (this.blocked) { if (this.blocked) {
this.block(); this.block();
} }
}, },
watch: {
blocked(newValue) {
if (newValue === true)
this.block();
else
this.unblock();
}
},
methods: { methods: {
block() { block() {
let styleClass = 'p-blockui p-component-overlay p-component-overlay-enter'; let styleClass = 'p-blockui p-component-overlay p-component-overlay-enter';
if (this.fullScreen) { if (this.fullScreen) {
styleClass += ' p-blockui-document'; styleClass += ' p-blockui-document';
this.mask = document.createElement('div'); this.mask = document.createElement('div');
@ -52,8 +51,7 @@ export default {
document.body.appendChild(this.mask); document.body.appendChild(this.mask);
DomHandler.addClass(document.body, 'p-overflow-hidden'); DomHandler.addClass(document.body, 'p-overflow-hidden');
document.activeElement.blur(); document.activeElement.blur();
} } else {
else {
this.mask = document.createElement('div'); this.mask = document.createElement('div');
this.mask.setAttribute('class', styleClass); this.mask.setAttribute('class', styleClass);
this.$refs.container.appendChild(this.mask); this.$refs.container.appendChild(this.mask);
@ -73,18 +71,18 @@ export default {
}, },
removeMask() { removeMask() {
ZIndexUtils.clear(this.mask); ZIndexUtils.clear(this.mask);
if (this.fullScreen) { if (this.fullScreen) {
document.body.removeChild(this.mask); document.body.removeChild(this.mask);
DomHandler.removeClass(document.body, 'p-overflow-hidden'); DomHandler.removeClass(document.body, 'p-overflow-hidden');
} } else {
else {
this.$refs.container.removeChild(this.mask); this.$refs.container.removeChild(this.mask);
} }
this.$emit('unblock'); this.$emit('unblock');
} }
} }
} };
</script> </script>
<style> <style>

View file

@ -31,14 +31,13 @@ export interface BreadcrumbSlots {
}) => VNode[]; }) => VNode[];
} }
export declare type BreadcrumbEmits = { export declare type BreadcrumbEmits = {};
}
declare class Breadcrumb extends ClassComponent<BreadcrumbProps, BreadcrumbSlots, BreadcrumbEmits> {} declare class Breadcrumb extends ClassComponent<BreadcrumbProps, BreadcrumbSlots, BreadcrumbEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Breadcrumb: GlobalComponentConstructor<Breadcrumb> Breadcrumb: GlobalComponentConstructor<Breadcrumb>;
} }
} }
@ -48,11 +47,11 @@ declare module '@vue/runtime-core' {
* *
* Helper API: * Helper API:
* *
* - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) * - [MenuItem](https://www.primefaces.org/primevue/menumodel)
* *
* Demos: * Demos:
* *
* - [Breadcrumb](https://www.primefaces.org/primevue/showcase/#/breadcrumb) * - [Breadcrumb](https://www.primefaces.org/primevue/breadcrumb)
* *
*/ */
export default Breadcrumb; export default Breadcrumb;

View file

@ -9,13 +9,7 @@ describe('Breadcrumb', () => {
}, },
props: { props: {
home: { icon: 'pi pi-home', to: '/' }, home: { icon: 'pi pi-home', to: '/' },
model: [ model: [{ label: 'Computer' }, { label: 'Notebook' }, { label: 'Accessories' }, { label: 'Backpacks' }, { label: 'Item' }]
{label: 'Computer'},
{label: 'Notebook'},
{label: 'Accessories'},
{label: 'Backpacks'},
{label: 'Item'}
]
} }
}); });

View file

@ -30,9 +30,9 @@ export default {
} }
}, },
components: { components: {
'BreadcrumbItem': BreadcrumbItem BreadcrumbItem: BreadcrumbItem
}
} }
};
</script> </script>
<style> <style>

View file

@ -1,7 +1,7 @@
<template> <template>
<li :class="containerClass(item)" v-if="visible()"> <li v-if="visible()" :class="containerClass(item)">
<template v-if="!template"> <template v-if="!template">
<router-link v-if="item.to" :to="item.to" custom v-slot="{navigate, href, isActive, isExactActive}"> <router-link v-if="item.to" v-slot="{ navigate, href, isActive, isExactActive }" :to="item.to" custom>
<a :href="href" :class="linkClass({ isActive, isExactActive })" @click="onClick($event, navigate)"> <a :href="href" :class="linkClass({ isActive, isExactActive })" @click="onClick($event, navigate)">
<span v-if="item.icon" :class="iconClass"></span> <span v-if="item.icon" :class="iconClass"></span>
<span v-if="item.label" class="p-menuitem-text">{{ label() }}</span> <span v-if="item.label" class="p-menuitem-text">{{ label() }}</span>
@ -41,19 +41,22 @@ export default {
return [{ 'p-disabled': this.disabled(item) }, this.item.class]; return [{ 'p-disabled': this.disabled(item) }, this.item.class];
}, },
linkClass(routerProps) { linkClass(routerProps) {
return ['p-menuitem-link', { return [
'p-menuitem-link',
{
'router-link-active': routerProps && routerProps.isActive, 'router-link-active': routerProps && routerProps.isActive,
'router-link-active-exact': this.exact && routerProps && routerProps.isExactActive 'router-link-active-exact': this.exact && routerProps && routerProps.isExactActive
}]; }
];
}, },
visible() { visible() {
return (typeof this.item.visible === 'function' ? this.item.visible() : this.item.visible !== false); return typeof this.item.visible === 'function' ? this.item.visible() : this.item.visible !== false;
}, },
disabled(item) { disabled(item) {
return (typeof item.disabled === 'function' ? item.disabled() : item.disabled); return typeof item.disabled === 'function' ? item.disabled() : item.disabled;
}, },
label() { label() {
return (typeof this.item.label === 'function' ? this.item.label() : this.item.label); return typeof this.item.label === 'function' ? this.item.label() : this.item.label;
} }
}, },
computed: { computed: {
@ -61,5 +64,5 @@ export default {
return ['p-menuitem-icon', this.item.icon]; return ['p-menuitem-icon', this.item.icon];
} }
} }
} };
</script> </script>

View file

@ -25,6 +25,10 @@ export interface ButtonProps extends ButtonHTMLAttributes {
* Default value is 'left'. * Default value is 'left'.
*/ */
iconPos?: ButtonIconPosType; iconPos?: ButtonIconPosType;
/**
* Style class of the icon.
*/
iconClass?: string | undefined;
/** /**
* Value of the badge. * Value of the badge.
*/ */
@ -51,14 +55,13 @@ export interface ButtonSlots {
default: () => VNode[]; default: () => VNode[];
} }
export declare type ButtonEmits = { export declare type ButtonEmits = {};
}
declare class Button extends ClassComponent<ButtonProps, ButtonSlots, ButtonEmits> {} declare class Button extends ClassComponent<ButtonProps, ButtonSlots, ButtonEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Button: GlobalComponentConstructor<Button> Button: GlobalComponentConstructor<Button>;
} }
} }
@ -68,7 +71,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Button](https://www.primefaces.org/primevue/showcase/#/button) * - [Button](https://www.primefaces.org/primevue/button)
* *
*/ */
export default Button; export default Button;

View file

@ -8,7 +8,7 @@ describe('Button.vue', () => {
expect(wrapper.find('.p-button.p-component').exists()).toBe(true); expect(wrapper.find('.p-button.p-component').exists()).toBe(true);
expect(wrapper.find('.p-button-label').exists()).toBe(true); expect(wrapper.find('.p-button-label').exists()).toBe(true);
}) });
}); });
describe('Button.vue', () => { describe('Button.vue', () => {
@ -30,7 +30,7 @@ describe('Button.vue', () => {
}); });
expect(wrapper.find('.p-button-icon.p-button-icon-' + iconPos).exists()).toBe(true); expect(wrapper.find('.p-button-icon.p-button-icon-' + iconPos).exists()).toBe(true);
}) });
}); });
describe('Button.vue', () => { describe('Button.vue', () => {
@ -41,10 +41,9 @@ describe('Button.vue', () => {
props: { badge, badgeClass } props: { badge, badgeClass }
}); });
expect(wrapper.find('.p-badge').text()).toEqual(badge); expect(wrapper.find('.p-badge').text()).toEqual(badge);
expect(wrapper.find('.' + badgeClass).exists()).toBe(true); expect(wrapper.find('.' + badgeClass).exists()).toBe(true);
}) });
}); });
describe('Button.vue', () => { describe('Button.vue', () => {
@ -59,7 +58,7 @@ describe('Button.vue', () => {
expect(wrapper.find('.p-disabled').exists()).toBe(false); expect(wrapper.find('.p-disabled').exists()).toBe(false);
await wrapper.setProps({ loading: true }) await wrapper.setProps({ loading: true });
const array = loadingIcon.split(' '); const array = loadingIcon.split(' ');
const lastIcon = '.' + array.join('.'); const lastIcon = '.' + array.join('.');
@ -69,7 +68,7 @@ describe('Button.vue', () => {
await wrapper.setProps({ loading: false }); await wrapper.setProps({ loading: false });
expect(wrapper.find('.p-button-loading').exists()).toBe(false); expect(wrapper.find('.p-button-loading').exists()).toBe(false);
}) });
}); });
describe('Button.vue', () => { describe('Button.vue', () => {
@ -81,5 +80,5 @@ describe('Button.vue', () => {
}); });
expect(wrapper.html()).toBe('<button class="p-button p-component" type="button"><span class="ml-2 font-bold">Default PrimeVue Button</span></button>'); expect(wrapper.html()).toBe('<button class="p-button p-component" type="button"><span class="ml-2 font-bold">Default PrimeVue Button</span></button>');
}) });
}); });

View file

@ -1,8 +1,8 @@
<template> <template>
<button :class="buttonClass" type="button" :aria-label="defaultAriaLabel" v-ripple :disabled="disabled"> <button v-ripple :class="buttonClass" type="button" :aria-label="defaultAriaLabel" :disabled="disabled">
<slot> <slot>
<span v-if="loading && !icon" :class="iconClass"></span> <span v-if="loading && !icon" :class="iconStyleClass"></span>
<span v-if="icon" :class="iconClass"></span> <span v-if="icon" :class="iconStyleClass"></span>
<span class="p-button-label">{{ label || '&nbsp;' }}</span> <span class="p-button-label">{{ label || '&nbsp;' }}</span>
<span v-if="badge" :class="badgeStyleClass">{{ badge }}</span> <span v-if="badge" :class="badgeStyleClass">{{ badge }}</span>
</slot> </slot>
@ -25,6 +25,10 @@ export default {
type: String, type: String,
default: 'left' default: 'left'
}, },
iconClass: {
type: String,
default: null
},
badge: { badge: {
type: String type: String
}, },
@ -50,35 +54,39 @@ export default {
'p-disabled': this.$attrs.disabled || this.loading, 'p-disabled': this.$attrs.disabled || this.loading,
'p-button-loading': this.loading, 'p-button-loading': this.loading,
'p-button-loading-label-only': this.loading && !this.icon && this.label 'p-button-loading-label-only': this.loading && !this.icon && this.label
} };
}, },
iconClass() { iconStyleClass() {
return [ return [
this.loading ? 'p-button-loading-icon ' + this.loadingIcon : this.icon, this.loading ? 'p-button-loading-icon ' + this.loadingIcon : this.icon,
'p-button-icon', 'p-button-icon',
this.iconClass,
{ {
'p-button-icon-left': this.iconPos === 'left' && this.label, 'p-button-icon-left': this.iconPos === 'left' && this.label,
'p-button-icon-right': this.iconPos === 'right' && this.label, 'p-button-icon-right': this.iconPos === 'right' && this.label,
'p-button-icon-top': this.iconPos === 'top' && this.label, 'p-button-icon-top': this.iconPos === 'top' && this.label,
'p-button-icon-bottom': this.iconPos === 'bottom' && this.label 'p-button-icon-bottom': this.iconPos === 'bottom' && this.label
} }
] ];
}, },
badgeStyleClass() { badgeStyleClass() {
return [ return [
'p-badge p-component', this.badgeClass, { 'p-badge p-component',
this.badgeClass,
{
'p-badge-no-gutter': this.badge && String(this.badge).length === 1 'p-badge-no-gutter': this.badge && String(this.badge).length === 1
}] }
];
}, },
disabled() { disabled() {
return this.$attrs.disabled || this.loading; return this.$attrs.disabled || this.loading;
}, },
defaultAriaLabel() { defaultAriaLabel() {
return (this.label ? this.label + (this.badge ? ' ' + this.badge : '') : this.$attrs['aria-label']); return this.label ? this.label + (this.badge ? ' ' + this.badge : '') : this.$attrs['aria-label'];
} }
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}
} }
};
</script> </script>

View file

@ -3,7 +3,7 @@ import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
type CalendarValueType = Date | Date[] | undefined; type CalendarValueType = Date | Date[] | undefined;
type CalendarSlotDateType = { day: number; month: number; year: number; today: boolean; selectable: boolean } type CalendarSlotDateType = { day: number; month: number; year: number; today: boolean; selectable: boolean };
type CalendarSelectionModeType = 'single' | 'multiple' | 'range' | undefined; type CalendarSelectionModeType = 'single' | 'multiple' | 'range' | undefined;
@ -330,7 +330,7 @@ export declare type CalendarEmits = {
* Callback to invoke when input field is being typed. * Callback to invoke when input field is being typed.
* @param {Event} event - Browser event * @param {Event} event - Browser event
*/ */
'input': (event: Event) => void; input: (event: Event) => void;
/** /**
* Callback to invoke when a date is selected. * Callback to invoke when a date is selected.
* @param {Date} value - Selected value. * @param {Date} value - Selected value.
@ -339,11 +339,11 @@ export declare type CalendarEmits = {
/** /**
* Callback to invoke when datepicker panel is shown. * Callback to invoke when datepicker panel is shown.
*/ */
'show': () => void; show: () => void;
/** /**
* Callback to invoke when datepicker panel is hidden. * Callback to invoke when datepicker panel is hidden.
*/ */
'hide': () => void; hide: () => void;
/** /**
* Callback to invoke when today button is clicked. * Callback to invoke when today button is clicked.
* @param {Date} date - Today as a date instance. * @param {Date} date - Today as a date instance.
@ -368,23 +368,23 @@ export declare type CalendarEmits = {
* Callback to invoke on focus of input field. * Callback to invoke on focus of input field.
* @param {Event} event - Focus event * @param {Event} event - Focus event
*/ */
'focus': (event: Event) => void; focus: (event: Event) => void;
/** /**
* Callback to invoke on blur of input field. * Callback to invoke on blur of input field.
* @param {CalendarBlurEvent} event - Blur event * @param {CalendarBlurEvent} event - Blur event
*/ */
'blur': (event: CalendarBlurEvent) => void; blur: (event: CalendarBlurEvent) => void;
/** /**
* Callback to invoke when a key is pressed. * Callback to invoke when a key is pressed.
*/ */
'keydown': (event: Event) => void; keydown: (event: Event) => void;
} };
declare class Calendar extends ClassComponent<CalendarProps, CalendarSlots, CalendarEmits> {} declare class Calendar extends ClassComponent<CalendarProps, CalendarSlots, CalendarEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Calendar: GlobalComponentConstructor<Calendar> Calendar: GlobalComponentConstructor<Calendar>;
} }
} }
@ -394,7 +394,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Calendar](https://www.primefaces.org/primevue/showcase/#/calendar) * - [Calendar](https://www.primefaces.org/primevue/calendar)
* *
*/ */
export default Calendar; export default Calendar;

View file

@ -40,14 +40,15 @@ describe('Calendar.vue', () => {
const onDateSelect = jest.spyOn(wrapper.vm, 'onDateSelect'); const onDateSelect = jest.spyOn(wrapper.vm, 'onDateSelect');
await wrapper.vm.onDateSelect({ currentTarget: { focus: () => {} } }, event); await wrapper.vm.onDateSelect({ currentTarget: { focus: () => {} } }, event);
expect(onDateSelect).toHaveBeenCalled() expect(onDateSelect).toHaveBeenCalled();
}); });
it('should calculate the correct view date when in range mode', async () => { it('should calculate the correct view date when in range mode', async () => {
const dateOne = new Date(); const dateOne = new Date();
const dateTwo = new Date(); const dateTwo = new Date();
dateTwo.setFullYear(dateOne.getFullYear(), dateOne.getMonth(), dateOne.getDate() + 1)
dateTwo.setFullYear(dateOne.getFullYear(), dateOne.getMonth(), dateOne.getDate() + 1);
await wrapper.setProps({ selectionMode: 'range', showTime: true, modelValue: [dateOne, dateTwo] }); await wrapper.setProps({ selectionMode: 'range', showTime: true, modelValue: [dateOne, dateTwo] });
expect(wrapper.vm.viewDate).toEqual(dateTwo) expect(wrapper.vm.viewDate).toEqual(dateTwo);
}); });
}); });

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,7 @@
import { VNode } from 'vue'; import { VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
export interface CardProps { export interface CardProps {}
}
export interface CardSlots { export interface CardSlots {
/** /**
@ -27,14 +26,13 @@ export interface CardSlots {
footer: () => VNode[]; footer: () => VNode[];
} }
export declare type CardEmits = { export declare type CardEmits = {};
}
declare class Card extends ClassComponent<CardProps, CardSlots, CardEmits> {} declare class Card extends ClassComponent<CardProps, CardSlots, CardEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Card: GlobalComponentConstructor<Card> Card: GlobalComponentConstructor<Card>;
} }
} }
@ -44,7 +42,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Card](https://www.primefaces.org/primevue/showcase/#/card) * - [Card](https://www.primefaces.org/primevue/card)
* *
*/ */
export default Card; export default Card;

View file

@ -1,15 +1,15 @@
<template> <template>
<div class="p-card p-component"> <div class="p-card p-component">
<div class="p-card-header" v-if="$slots.header"> <div v-if="$slots.header" class="p-card-header">
<slot name="header"></slot> <slot name="header"></slot>
</div> </div>
<div class="p-card-body"> <div class="p-card-body">
<div class="p-card-title" v-if="$slots.title"><slot name="title"></slot></div> <div v-if="$slots.title" class="p-card-title"><slot name="title"></slot></div>
<div class="p-card-subtitle" v-if="$slots.subtitle"><slot name="subtitle"></slot></div> <div v-if="$slots.subtitle" class="p-card-subtitle"><slot name="subtitle"></slot></div>
<div class="p-card-content"> <div class="p-card-content">
<slot name="content"></slot> <slot name="content"></slot>
</div> </div>
<div class="p-card-footer" v-if="$slots.footer"> <div v-if="$slots.footer" class="p-card-footer">
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>
</div> </div>
@ -19,7 +19,7 @@
<script> <script>
export default { export default {
name: 'Card' name: 'Card'
} };
</script> </script>
<style> <style>

View file

@ -75,6 +75,16 @@ export interface CarouselProps {
* Default value is 0. * Default value is 0.
*/ */
autoplayInterval?: number | undefined; autoplayInterval?: number | undefined;
/**
* Whether to display navigation buttons in container.
* Default value is true.
*/
showNavigators?: boolean | undefined;
/**
* Whether to display indicator container.
* Default value is true.
*/
showIndicators?: boolean | undefined;
} }
export interface CarouselSlots { export interface CarouselSlots {
@ -108,13 +118,13 @@ export declare type CarouselEmits = {
* @param {number} value - New page value. * @param {number} value - New page value.
*/ */
'update:page': (value: number) => void; 'update:page': (value: number) => void;
} };
declare class Carousel extends ClassComponent<CarouselProps, CarouselSlots, CarouselEmits> {} declare class Carousel extends ClassComponent<CarouselProps, CarouselSlots, CarouselEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Carousel: GlobalComponentConstructor<Carousel> Carousel: GlobalComponentConstructor<Carousel>;
} }
} }
@ -124,7 +134,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Carousel](https://www.primefaces.org/primevue/showcase/#/carousel) * - [Carousel](https://www.primefaces.org/primevue/carousel)
* *
*/ */
export default Carousel; export default Carousel;

View file

@ -7,52 +7,52 @@ describe('Carousel.vue', () => {
props: { props: {
value: [ value: [
{ {
"id": "1000", id: '1000',
"code": "vbb124btr", code: 'vbb124btr',
"name": "Game Controller", name: 'Game Controller',
"description": "Product Description", description: 'Product Description',
"image": "game-controller.jpg", image: 'game-controller.jpg',
"price": 99, price: 99,
"category": "Electronics", category: 'Electronics',
"quantity": 2, quantity: 2,
"inventoryStatus": "LOWSTOCK", inventoryStatus: 'LOWSTOCK',
"rating": 4 rating: 4
}, },
{ {
"id": "1001", id: '1001',
"code": "nvklal433", code: 'nvklal433',
"name": "Black Watch", name: 'Black Watch',
"description": "Product Description", description: 'Product Description',
"image": "black-watch.jpg", image: 'black-watch.jpg',
"price": 72, price: 72,
"category": "Accessories", category: 'Accessories',
"quantity": 61, quantity: 61,
"inventoryStatus": "INSTOCK", inventoryStatus: 'INSTOCK',
"rating": 4 rating: 4
}, },
{ {
"id": "1002", id: '1002',
"code": "zz21cz3c1", code: 'zz21cz3c1',
"name": "Blue Band", name: 'Blue Band',
"description": "Product Description", description: 'Product Description',
"image": "blue-band.jpg", image: 'blue-band.jpg',
"price": 79, price: 79,
"category": "Fitness", category: 'Fitness',
"quantity": 2, quantity: 2,
"inventoryStatus": "LOWSTOCK", inventoryStatus: 'LOWSTOCK',
"rating": 3 rating: 3
}, },
{ {
"id": "1003", id: '1003',
"code": "244wgerg2", code: '244wgerg2',
"name": "Blue T-Shirt", name: 'Blue T-Shirt',
"description": "Product Description", description: 'Product Description',
"image": "blue-t-shirt.jpg", image: 'blue-t-shirt.jpg',
"price": 29, price: 29,
"category": "Clothing", category: 'Clothing',
"quantity": 25, quantity: 25,
"inventoryStatus": "INSTOCK", inventoryStatus: 'INSTOCK',
"rating": 5 rating: 5
} }
] ]
}, },
@ -74,11 +74,13 @@ describe('Carousel.vue', () => {
expect(wrapper.findAll('.p-carousel-item').length).toBe(4); expect(wrapper.findAll('.p-carousel-item').length).toBe(4);
const firstItem = wrapper.findAll('.p-carousel-item')[0]; const firstItem = wrapper.findAll('.p-carousel-item')[0];
expect(firstItem.classes()).toContain('p-carousel-item-active'); expect(firstItem.classes()).toContain('p-carousel-item-active');
const nextBtn = wrapper.find('.p-carousel-next'); const nextBtn = wrapper.find('.p-carousel-next');
await nextBtn.trigger('click'); await nextBtn.trigger('click');
expect(firstItem.classes()).not.toContain('p-carousel-item-active'); expect(firstItem.classes()).not.toContain('p-carousel-item-active');
}) });
}); });

View file

@ -1,52 +1,58 @@
<template> <template>
<div :id="id" :class="['p-carousel p-component', { 'p-carousel-vertical': isVertical(), 'p-carousel-horizontal': !isVertical() }]"> <div :id="id" :class="['p-carousel p-component', { 'p-carousel-vertical': isVertical(), 'p-carousel-horizontal': !isVertical() }]">
<div class="p-carousel-header" v-if="$slots.header"> <div v-if="$slots.header" class="p-carousel-header">
<slot name="header"></slot> <slot name="header"></slot>
</div> </div>
<div :class="contentClasses"> <div :class="contentClasses">
<div :class="containerClasses"> <div :class="containerClasses">
<button :class="['p-carousel-prev p-link', {'p-disabled': backwardIsDisabled}]" :disabled="backwardIsDisabled" @click="navBackward" type="button" v-ripple> <button v-if="showNavigators" v-ripple :class="['p-carousel-prev p-link', { 'p-disabled': backwardIsDisabled }]" :disabled="backwardIsDisabled" @click="navBackward" type="button">
<span :class="['p-carousel-prev-icon pi', { 'pi-chevron-left': !isVertical(), 'pi-chevron-up': isVertical() }]"></span> <span :class="['p-carousel-prev-icon pi', { 'pi-chevron-left': !isVertical(), 'pi-chevron-up': isVertical() }]"></span>
</button> </button>
<div class="p-carousel-items-content" :style="[{'height': isVertical() ? verticalViewPortHeight : 'auto'}]" @touchend="onTouchEnd" @touchstart="onTouchStart" @touchmove="onTouchMove"> <div class="p-carousel-items-content" :style="[{ height: isVertical() ? verticalViewPortHeight : 'auto' }]" @touchend="onTouchEnd" @touchstart="onTouchStart" @touchmove="onTouchMove">
<div ref="itemsContainer" class="p-carousel-items-container" @transitionend="onTransitionEnd"> <div ref="itemsContainer" class="p-carousel-items-container" @transitionend="onTransitionEnd">
<template v-if="isCircular()"> <template v-if="isCircular()">
<div v-for="(item, index) of value.slice(-1 * d_numVisible)" :key="index + '_scloned'" :class="['p-carousel-item p-carousel-item-cloned', <div
{'p-carousel-item-active': (totalShiftedItems * -1) === (value.length + d_numVisible), v-for="(item, index) of value.slice(-1 * d_numVisible)"
'p-carousel-item-start': 0 === index, :key="index + '_scloned'"
'p-carousel-item-end': value.slice(-1 * d_numVisible).length - 1 === index}]"> :class="[
'p-carousel-item p-carousel-item-cloned',
{ 'p-carousel-item-active': totalShiftedItems * -1 === value.length + d_numVisible, 'p-carousel-item-start': 0 === index, 'p-carousel-item-end': value.slice(-1 * d_numVisible).length - 1 === index }
]"
>
<slot name="item" :data="item" :index="index"></slot> <slot name="item" :data="item" :index="index"></slot>
</div> </div>
</template> </template>
<div v-for="(item, index) of value" :key="index" :class="['p-carousel-item', <div
{'p-carousel-item-active': firstIndex() <= index && lastIndex() >= index, v-for="(item, index) of value"
'p-carousel-item-start': firstIndex() === index, :key="index"
'p-carousel-item-end': lastIndex() === index}]"> :class="['p-carousel-item', { 'p-carousel-item-active': firstIndex() <= index && lastIndex() >= index, 'p-carousel-item-start': firstIndex() === index, 'p-carousel-item-end': lastIndex() === index }]"
>
<slot name="item" :data="item" :index="index"></slot> <slot name="item" :data="item" :index="index"></slot>
</div> </div>
<template v-if="isCircular()"> <template v-if="isCircular()">
<div v-for="(item, index) of value.slice(0, d_numVisible)" :key="index + '_fcloned'" :class="['p-carousel-item p-carousel-item-cloned', <div
{'p-carousel-item-active': totalShiftedItems === 0, v-for="(item, index) of value.slice(0, d_numVisible)"
'p-carousel-item-start': 0 === index, :key="index + '_fcloned'"
'p-carousel-item-end': value.slice(0, d_numVisible).length - 1 === index}]"> :class="['p-carousel-item p-carousel-item-cloned', { 'p-carousel-item-active': totalShiftedItems === 0, 'p-carousel-item-start': 0 === index, 'p-carousel-item-end': value.slice(0, d_numVisible).length - 1 === index }]"
>
<slot name="item" :data="item" :index="index"></slot> <slot name="item" :data="item" :index="index"></slot>
</div> </div>
</template> </template>
</div> </div>
</div> </div>
<button :class="['p-carousel-next p-link', {'p-disabled': forwardIsDisabled}]" :disabled="forwardIsDisabled" @click="navForward" type="button" v-ripple> <button v-if="showNavigators" v-ripple :class="['p-carousel-next p-link', { 'p-disabled': forwardIsDisabled }]" :disabled="forwardIsDisabled" @click="navForward" type="button">
<span :class="['p-carousel-prev-icon pi', { 'pi-chevron-right': !isVertical(), 'pi-chevron-down': isVertical() }]"></span> <span :class="['p-carousel-prev-icon pi', { 'pi-chevron-right': !isVertical(), 'pi-chevron-down': isVertical() }]"></span>
</button> </button>
</div> </div>
<ul v-if="totalIndicators >= 0" :class="indicatorsContentClasses"> <ul v-if="totalIndicators >= 0 && showIndicators" :class="indicatorsContentClasses">
<li v-for="(indicator, i) of totalIndicators" :key="'p-carousel-indicator-' + i.toString()" :class="['p-carousel-indicator', { 'p-highlight': d_page === i }]"> <li v-for="(indicator, i) of totalIndicators" :key="'p-carousel-indicator-' + i.toString()" :class="['p-carousel-indicator', { 'p-highlight': d_page === i }]">
<button class="p-link" @click="onIndicatorClick($event, i)" type="button" /> <button class="p-link" @click="onIndicatorClick($event, i)" type="button" />
</li> </li>
</ul> </ul>
</div> </div>
<div class="p-carousel-footer" v-if="$slots.footer"> <div v-if="$slots.footer" class="p-carousel-footer">
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>
</div> </div>
@ -93,6 +99,14 @@ export default {
autoplayInterval: { autoplayInterval: {
type: Number, type: Number,
default: 0 default: 0
},
showNavigators: {
type: Boolean,
default: true
},
showIndicators: {
type: Boolean,
default: true
} }
}, },
data() { data() {
@ -109,7 +123,7 @@ export default {
allowAutoplay: !!this.autoplayInterval, allowAutoplay: !!this.autoplayInterval,
d_circular: this.circular || this.allowAutoplay, d_circular: this.circular || this.allowAutoplay,
swipeThreshold: 20 swipeThreshold: 20
} };
}, },
isRemainingItemsAdded: false, isRemainingItemsAdded: false,
watch: { watch: {
@ -131,41 +145,154 @@ export default {
this.d_oldValue = oldValue; this.d_oldValue = oldValue;
} }
}, },
mounted() {
let stateChanged = false;
this.createStyle();
this.calculatePosition();
if (this.responsiveOptions) {
this.bindDocumentListeners();
}
if (this.isCircular()) {
let totalShiftedItems = this.totalShiftedItems;
if (this.d_page === 0) {
totalShiftedItems = -1 * this.d_numVisible;
} else if (totalShiftedItems === 0) {
totalShiftedItems = -1 * this.value.length;
if (this.remainingItems > 0) {
this.isRemainingItemsAdded = true;
}
}
if (totalShiftedItems !== this.totalShiftedItems) {
this.totalShiftedItems = totalShiftedItems;
stateChanged = true;
}
}
if (!stateChanged && this.isAutoplay()) {
this.startAutoplay();
}
},
updated() {
const isCircular = this.isCircular();
let stateChanged = false;
let totalShiftedItems = this.totalShiftedItems;
if (this.autoplayInterval) {
this.stopAutoplay();
}
if (this.d_oldNumScroll !== this.d_numScroll || this.d_oldNumVisible !== this.d_numVisible || this.d_oldValue.length !== this.value.length) {
this.remainingItems = (this.value.length - this.d_numVisible) % this.d_numScroll;
let page = this.d_page;
if (this.totalIndicators !== 0 && page >= this.totalIndicators) {
page = this.totalIndicators - 1;
this.$emit('update:page', page);
this.d_page = page;
stateChanged = true;
}
totalShiftedItems = page * this.d_numScroll * -1;
if (isCircular) {
totalShiftedItems -= this.d_numVisible;
}
if (page === this.totalIndicators - 1 && this.remainingItems > 0) {
totalShiftedItems += -1 * this.remainingItems + this.d_numScroll;
this.isRemainingItemsAdded = true;
} else {
this.isRemainingItemsAdded = false;
}
if (totalShiftedItems !== this.totalShiftedItems) {
this.totalShiftedItems = totalShiftedItems;
stateChanged = true;
}
this.d_oldNumScroll = this.d_numScroll;
this.d_oldNumVisible = this.d_numVisible;
this.d_oldValue = this.value;
this.$refs.itemsContainer.style.transform = this.isVertical() ? `translate3d(0, ${totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`;
}
if (isCircular) {
if (this.d_page === 0) {
totalShiftedItems = -1 * this.d_numVisible;
} else if (totalShiftedItems === 0) {
totalShiftedItems = -1 * this.value.length;
if (this.remainingItems > 0) {
this.isRemainingItemsAdded = true;
}
}
if (totalShiftedItems !== this.totalShiftedItems) {
this.totalShiftedItems = totalShiftedItems;
stateChanged = true;
}
}
if (!stateChanged && this.isAutoplay()) {
this.startAutoplay();
}
},
beforeUnmount() {
if (this.responsiveOptions) {
this.unbindDocumentListeners();
}
if (this.autoplayInterval) {
this.stopAutoplay();
}
},
methods: { methods: {
step(dir, page) { step(dir, page) {
let totalShiftedItems = this.totalShiftedItems; let totalShiftedItems = this.totalShiftedItems;
const isCircular = this.isCircular(); const isCircular = this.isCircular();
if (page != null) { if (page != null) {
totalShiftedItems = (this.d_numScroll * page) * -1; totalShiftedItems = this.d_numScroll * page * -1;
if (isCircular) { if (isCircular) {
totalShiftedItems -= this.d_numVisible; totalShiftedItems -= this.d_numVisible;
} }
this.isRemainingItemsAdded = false; this.isRemainingItemsAdded = false;
} } else {
else { totalShiftedItems += this.d_numScroll * dir;
totalShiftedItems += (this.d_numScroll * dir);
if (this.isRemainingItemsAdded) { if (this.isRemainingItemsAdded) {
totalShiftedItems += this.remainingItems - (this.d_numScroll * dir); totalShiftedItems += this.remainingItems - this.d_numScroll * dir;
this.isRemainingItemsAdded = false; this.isRemainingItemsAdded = false;
} }
let originalShiftedItems = isCircular ? (totalShiftedItems + this.d_numVisible) : totalShiftedItems; let originalShiftedItems = isCircular ? totalShiftedItems + this.d_numVisible : totalShiftedItems;
page = Math.abs(Math.floor(originalShiftedItems / this.d_numScroll)); page = Math.abs(Math.floor(originalShiftedItems / this.d_numScroll));
} }
if (isCircular && this.d_page === (this.totalIndicators - 1) && dir === -1) { if (isCircular && this.d_page === this.totalIndicators - 1 && dir === -1) {
totalShiftedItems = -1 * (this.value.length + this.d_numVisible); totalShiftedItems = -1 * (this.value.length + this.d_numVisible);
page = 0; page = 0;
} } else if (isCircular && this.d_page === 0 && dir === 1) {
else if (isCircular && this.d_page === 0 && dir === 1) {
totalShiftedItems = 0; totalShiftedItems = 0;
page = (this.totalIndicators - 1); page = this.totalIndicators - 1;
} } else if (page === this.totalIndicators - 1 && this.remainingItems > 0) {
else if (page === (this.totalIndicators - 1) && this.remainingItems > 0) { totalShiftedItems += this.remainingItems * -1 - this.d_numScroll * dir;
totalShiftedItems += ((this.remainingItems * -1) - (this.d_numScroll * dir));
this.isRemainingItemsAdded = true; this.isRemainingItemsAdded = true;
} }
@ -198,9 +325,10 @@ export default {
if (this.d_numScroll !== matchedResponsiveOptionsData.numScroll) { if (this.d_numScroll !== matchedResponsiveOptionsData.numScroll) {
let page = this.d_page; let page = this.d_page;
page = parseInt((page * this.d_numScroll) / matchedResponsiveOptionsData.numScroll); page = parseInt((page * this.d_numScroll) / matchedResponsiveOptionsData.numScroll);
this.totalShiftedItems = (matchedResponsiveOptionsData.numScroll * page) * -1; this.totalShiftedItems = matchedResponsiveOptionsData.numScroll * page * -1;
if (this.isCircular()) { if (this.isCircular()) {
this.totalShiftedItems -= matchedResponsiveOptionsData.numVisible; this.totalShiftedItems -= matchedResponsiveOptionsData.numVisible;
@ -229,7 +357,7 @@ export default {
} }
}, },
navForward(e, index) { navForward(e, index) {
if (this.d_circular || this.d_page < (this.totalIndicators - 1)) { if (this.d_circular || this.d_page < this.totalIndicators - 1) {
this.step(-1, index); this.step(-1, index);
} }
@ -244,8 +372,7 @@ export default {
if (index > page) { if (index > page) {
this.navForward(e, index); this.navForward(e, index);
} } else if (index < page) {
else if (index < page) {
this.navBackward(e, index); this.navBackward(e, index);
} }
}, },
@ -254,7 +381,7 @@ export default {
DomHandler.addClass(this.$refs.itemsContainer, 'p-items-hidden'); DomHandler.addClass(this.$refs.itemsContainer, 'p-items-hidden');
this.$refs.itemsContainer.style.transition = ''; this.$refs.itemsContainer.style.transition = '';
if ((this.d_page === 0 || this.d_page === (this.totalIndicators - 1)) && this.isCircular()) { if ((this.d_page === 0 || this.d_page === this.totalIndicators - 1) && this.isCircular()) {
this.$refs.itemsContainer.style.transform = this.isVertical() ? `translate3d(0, ${this.totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${this.totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`; this.$refs.itemsContainer.style.transform = this.isVertical() ? `translate3d(0, ${this.totalShiftedItems * (100 / this.d_numVisible)}%, 0)` : `translate3d(${this.totalShiftedItems * (100 / this.d_numVisible)}%, 0, 0)`;
} }
} }
@ -276,18 +403,18 @@ export default {
let touchobj = e.changedTouches[0]; let touchobj = e.changedTouches[0];
if (this.isVertical()) { if (this.isVertical()) {
this.changePageOnTouch(e, (touchobj.pageY - this.startPos.y)); this.changePageOnTouch(e, touchobj.pageY - this.startPos.y);
} } else {
else { this.changePageOnTouch(e, touchobj.pageX - this.startPos.x);
this.changePageOnTouch(e, (touchobj.pageX - this.startPos.x));
} }
}, },
changePageOnTouch(e, diff) { changePageOnTouch(e, diff) {
if (Math.abs(diff) > this.swipeThreshold) { if (Math.abs(diff) > this.swipeThreshold) {
if (diff < 0) { // left if (diff < 0) {
// left
this.navForward(e); this.navForward(e);
} } else {
else { // right // right
this.navBackward(e); this.navBackward(e);
} }
} }
@ -309,14 +436,12 @@ export default {
}, },
startAutoplay() { startAutoplay() {
this.interval = setInterval(() => { this.interval = setInterval(() => {
if(this.d_page === (this.totalIndicators - 1)) { if (this.d_page === this.totalIndicators - 1) {
this.step(-1, 0); this.step(-1, 0);
} } else {
else {
this.step(-1, this.d_page + 1); this.step(-1, this.d_page + 1);
} }
}, }, this.autoplayInterval);
this.autoplayInterval);
}, },
stopAutoplay() { stopAutoplay() {
if (this.interval) { if (this.interval) {
@ -332,27 +457,23 @@ export default {
let innerHTML = ` let innerHTML = `
#${this.id} .p-carousel-item { #${this.id} .p-carousel-item {
flex: 1 0 ${ (100/ this.d_numVisible) }% flex: 1 0 ${100 / this.d_numVisible}%
} }
`; `;
if (this.responsiveOptions) { if (this.responsiveOptions) {
let _responsiveOptions = [...this.responsiveOptions]; let _responsiveOptions = [...this.responsiveOptions];
_responsiveOptions.sort((data1, data2) => { _responsiveOptions.sort((data1, data2) => {
const value1 = data1.breakpoint; const value1 = data1.breakpoint;
const value2 = data2.breakpoint; const value2 = data2.breakpoint;
let result = null; let result = null;
if (value1 == null && value2 != null) if (value1 == null && value2 != null) result = -1;
result = -1; else if (value1 != null && value2 == null) result = 1;
else if (value1 != null && value2 == null) else if (value1 == null && value2 == null) result = 0;
result = 1; else if (typeof value1 === 'string' && typeof value2 === 'string') result = value1.localeCompare(value2, undefined, { numeric: true });
else if (value1 == null && value2 == null) else result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0;
result = 0;
else if (typeof value1 === 'string' && typeof value2 === 'string')
result = value1.localeCompare(value2, undefined, { numeric: true });
else
result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
return -1 * result; return -1 * result;
}); });
@ -363,10 +484,10 @@ export default {
innerHTML += ` innerHTML += `
@media screen and (max-width: ${res.breakpoint}) { @media screen and (max-width: ${res.breakpoint}) {
#${this.id} .p-carousel-item { #${this.id} .p-carousel-item {
flex: 1 0 ${ (100/ res.numVisible) }% flex: 1 0 ${100 / res.numVisible}%
} }
} }
` `;
} }
} }
@ -382,122 +503,10 @@ export default {
return this.autoplayInterval && this.allowAutoplay; return this.autoplayInterval && this.allowAutoplay;
}, },
firstIndex() { firstIndex() {
return this.isCircular()? (-1 * (this.totalShiftedItems + this.d_numVisible)) : (this.totalShiftedItems * -1); return this.isCircular() ? -1 * (this.totalShiftedItems + this.d_numVisible) : this.totalShiftedItems * -1;
}, },
lastIndex() { lastIndex() {
return (this.firstIndex() + this.d_numVisible - 1); return this.firstIndex() + this.d_numVisible - 1;
}
},
mounted() {
let stateChanged = false;
this.createStyle();
this.calculatePosition();
if (this.responsiveOptions) {
this.bindDocumentListeners();
}
if (this.isCircular()) {
let totalShiftedItems = this.totalShiftedItems;
if (this.d_page === 0) {
totalShiftedItems = -1 * this.d_numVisible;
}
else if (totalShiftedItems === 0) {
totalShiftedItems = -1 * this.value.length;
if (this.remainingItems > 0) {
this.isRemainingItemsAdded = true;
}
}
if (totalShiftedItems !== this.totalShiftedItems) {
this.totalShiftedItems = totalShiftedItems;
stateChanged = true;
}
}
if (!stateChanged && this.isAutoplay()) {
this.startAutoplay();
}
},
updated() {
const isCircular = this.isCircular();
let stateChanged = false;
let totalShiftedItems = this.totalShiftedItems;
if (this.autoplayInterval) {
this.stopAutoplay();
}
if(this.d_oldNumScroll !== this.d_numScroll || this.d_oldNumVisible !== this.d_numVisible || this.d_oldValue.length !== this.value.length) {
this.remainingItems = (this.value.length - this.d_numVisible) % this.d_numScroll;
let page = this.d_page;
if (this.totalIndicators !== 0 && page >= this.totalIndicators) {
page = this.totalIndicators - 1;
this.$emit('update:page', page);
this.d_page = page;
stateChanged = true;
}
totalShiftedItems = (page * this.d_numScroll) * -1;
if (isCircular) {
totalShiftedItems -= this.d_numVisible;
}
if (page === (this.totalIndicators - 1) && this.remainingItems > 0) {
totalShiftedItems += (-1 * this.remainingItems) + this.d_numScroll;
this.isRemainingItemsAdded = true;
}
else {
this.isRemainingItemsAdded = false;
}
if (totalShiftedItems !== this.totalShiftedItems) {
this.totalShiftedItems = totalShiftedItems;
stateChanged = true;
}
this.d_oldNumScroll = this.d_numScroll;
this.d_oldNumVisible = this.d_numVisible;
this.d_oldValue = this.value;
this.$refs.itemsContainer.style.transform = this.isVertical() ? `translate3d(0, ${totalShiftedItems * (100/ this.d_numVisible)}%, 0)` : `translate3d(${totalShiftedItems * (100/ this.d_numVisible)}%, 0, 0)`;
}
if (isCircular) {
if (this.d_page === 0) {
totalShiftedItems = -1 * this.d_numVisible;
}
else if (totalShiftedItems === 0) {
totalShiftedItems = -1 * this.value.length;
if (this.remainingItems > 0) {
this.isRemainingItemsAdded = true;
}
}
if (totalShiftedItems !== this.totalShiftedItems) {
this.totalShiftedItems = totalShiftedItems;
stateChanged = true;
}
}
if (!stateChanged && this.isAutoplay()) {
this.startAutoplay();
}
},
beforeUnmount() {
if (this.responsiveOptions) {
this.unbindDocumentListeners();
}
if (this.autoplayInterval) {
this.stopAutoplay();
} }
}, },
computed: { computed: {
@ -505,10 +514,10 @@ export default {
return this.value ? Math.max(Math.ceil((this.value.length - this.d_numVisible) / this.d_numScroll) + 1, 0) : 0; return this.value ? Math.max(Math.ceil((this.value.length - this.d_numVisible) / this.d_numScroll) + 1, 0) : 0;
}, },
backwardIsDisabled() { backwardIsDisabled() {
return (this.value && (!this.circular || this.value.length < this.d_numVisible) && this.d_page === 0); return this.value && (!this.circular || this.value.length < this.d_numVisible) && this.d_page === 0;
}, },
forwardIsDisabled() { forwardIsDisabled() {
return (this.value && (!this.circular || this.value.length < this.d_numVisible) && (this.d_page === (this.totalIndicators - 1) || this.totalIndicators === 0)); return this.value && (!this.circular || this.value.length < this.d_numVisible) && (this.d_page === this.totalIndicators - 1 || this.totalIndicators === 0);
}, },
containerClasses() { containerClasses() {
return ['p-carousel-container', this.containerClass]; return ['p-carousel-container', this.containerClass];
@ -518,12 +527,12 @@ export default {
}, },
indicatorsContentClasses() { indicatorsContentClasses() {
return ['p-carousel-indicators p-reset', this.indicatorsContentClass]; return ['p-carousel-indicators p-reset', this.indicatorsContentClass];
}, }
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}
} }
};
</script> </script>
<style> <style>

View file

@ -121,6 +121,11 @@ export interface CascadeSelectProps {
* Default value is true. * Default value is true.
*/ */
autoOptionFocus?: boolean | undefined; autoOptionFocus?: boolean | undefined;
/**
* When enabled, the focused option is selected/opened.
* Default value is false.
*/
selectOnFocus?: boolean | undefined;
/** /**
* Locale to use in searching. The default locale is the host environment's current locale. * Locale to use in searching. The default locale is the host environment's current locale.
*/ */
@ -205,22 +210,22 @@ export declare type CascadeSelectEmits = {
* Callback to invoke on value change. * Callback to invoke on value change.
* @param { CascadeSelectChangeEvent } event - Custom change event. * @param { CascadeSelectChangeEvent } event - Custom change event.
*/ */
'change': (event: CascadeSelectChangeEvent) => void; change: (event: CascadeSelectChangeEvent) => void;
/** /**
* Callback to invoke when the component receives focus. * Callback to invoke when the component receives focus.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'focus': (event: Event) => void; focus: (event: Event) => void;
/** /**
* Callback to invoke when the component loses focus. * Callback to invoke when the component loses focus.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'blur': (event: Event) => void; blur: (event: Event) => void;
/** /**
* Callback to invoke on click. * Callback to invoke on click.
* @param { Event } event - Browser event. * @param { Event } event - Browser event.
*/ */
'click': (event: Event) => void; click: (event: Event) => void;
/** /**
* Callback to invoke when a group changes. * Callback to invoke when a group changes.
* @param { CascadeSelectGroupChangeEvent } event - Custom change event. * @param { CascadeSelectGroupChangeEvent } event - Custom change event.
@ -237,18 +242,18 @@ export declare type CascadeSelectEmits = {
/** /**
* Callback to invoke when the overlay is shown. * Callback to invoke when the overlay is shown.
*/ */
'show': () => void; show: () => void;
/** /**
* Callback to invoke when the overlay is hidden. * Callback to invoke when the overlay is hidden.
*/ */
'hide': () => void; hide: () => void;
} };
declare class CascadeSelect extends ClassComponent<CascadeSelectProps, CascadeSelectSlots, CascadeSelectEmits> {} declare class CascadeSelect extends ClassComponent<CascadeSelectProps, CascadeSelectSlots, CascadeSelectEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
CascadeSelect: GlobalComponentConstructor<CascadeSelect> CascadeSelect: GlobalComponentConstructor<CascadeSelect>;
} }
} }
@ -258,7 +263,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [CascadeSelect](https://www.primefaces.org/primevue/showcase/#/cascadeselect) * - [CascadeSelect](https://www.primefaces.org/primevue/cascadeselect)
* *
*/ */
export default CascadeSelect; export default CascadeSelect;

View file

@ -34,8 +34,7 @@ describe('CascadeSelect.vue', () => {
{ cname: 'Brisbane', code: 'A-BR' }, { cname: 'Brisbane', code: 'A-BR' },
{ cname: 'Townsville', code: 'A-TO' } { cname: 'Townsville', code: 'A-TO' }
] ]
}, }
] ]
}, },
{ {
@ -55,8 +54,7 @@ describe('CascadeSelect.vue', () => {
{ cname: 'Ottawa', code: 'C-OT' }, { cname: 'Ottawa', code: 'C-OT' },
{ cname: 'Toronto', code: 'C-TO' } { cname: 'Toronto', code: 'C-TO' }
] ]
}, }
] ]
}, },
{ {
@ -112,6 +110,7 @@ describe('CascadeSelect.vue', () => {
expect(wrapper.findAll('.p-cascadeselect-item-text')[0].text()).toBe('Australia'); expect(wrapper.findAll('.p-cascadeselect-item-text')[0].text()).toBe('Australia');
const firstGroup = wrapper.findAll('.p-cascadeselect-item-content')[0]; const firstGroup = wrapper.findAll('.p-cascadeselect-item-content')[0];
await firstGroup.trigger('click'); await firstGroup.trigger('click');
expect(wrapper.find('.p-cascadeselect-panel.p-cascadeselect-sublist').exists()).toBe(true); expect(wrapper.find('.p-cascadeselect-panel.p-cascadeselect-sublist').exists()).toBe(true);

View file

@ -1,9 +1,28 @@
<template> <template>
<div ref="container" :class="containerClass" @click="onContainerClick($event)"> <div ref="container" :class="containerClass" @click="onContainerClick($event)">
<div class="p-hidden-accessible"> <div class="p-hidden-accessible">
<input ref="focusInput" :id="inputId" type="text" :style="inputStyle" :class="inputClass" readonly :disabled="disabled" :placeholder="placeholder" :tabindex="!disabled ? tabindex : -1" <input
role="combobox" :aria-label="ariaLabel" :aria-labelledby="ariaLabelledby" aria-haspopup="tree" :aria-expanded="overlayVisible" :aria-controls="id + '_tree'" :aria-activedescendant="focused ? focusedOptionId : undefined" ref="focusInput"
@focus="onFocus" @blur="onBlur" @keydown="onKeyDown" v-bind="inputProps" /> :id="inputId"
type="text"
:style="inputStyle"
:class="inputClass"
readonly
:disabled="disabled"
:placeholder="placeholder"
:tabindex="!disabled ? tabindex : -1"
role="combobox"
:aria-label="ariaLabel"
:aria-labelledby="ariaLabelledby"
aria-haspopup="tree"
:aria-expanded="overlayVisible"
:aria-controls="id + '_tree'"
:aria-activedescendant="focused ? focusedOptionId : undefined"
@focus="onFocus"
@blur="onBlur"
@keydown="onKeyDown"
v-bind="inputProps"
/>
</div> </div>
<span :class="labelClass"> <span :class="labelClass">
<slot name="value" :value="modelValue" :placeholder="placeholder"> <slot name="value" :value="modelValue" :placeholder="placeholder">
@ -22,9 +41,23 @@
<transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave"> <transition name="p-connected-overlay" @enter="onOverlayEnter" @after-enter="onOverlayAfterEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave">
<div v-if="overlayVisible" :ref="overlayRef" :style="panelStyle" :class="panelStyleClass" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="panelProps"> <div v-if="overlayVisible" :ref="overlayRef" :style="panelStyle" :class="panelStyleClass" @click="onOverlayClick" @keydown="onOverlayKeyDown" v-bind="panelProps">
<div class="p-cascadeselect-items-wrapper"> <div class="p-cascadeselect-items-wrapper">
<CascadeSelectSub :id="id + '_tree'" role="tree" aria-orientation="horizontal" :selectId="id" :focusedOptionId="focused ? focusedOptionId : undefined" <CascadeSelectSub
:options="processedOptions" :activeOptionPath="activeOptionPath" :level="0" :templates="$slots" :optionLabel="optionLabel" :optionValue="optionValue" :optionDisabled="optionDisabled" :id="id + '_tree'"
:optionGroupLabel="optionGroupLabel" :optionGroupChildren="optionGroupChildren" @option-change="onOptionChange" /> role="tree"
aria-orientation="horizontal"
:selectId="id"
:focusedOptionId="focused ? focusedOptionId : undefined"
:options="processedOptions"
:activeOptionPath="activeOptionPath"
:level="0"
:templates="$slots"
:optionLabel="optionLabel"
:optionValue="optionValue"
:optionDisabled="optionDisabled"
:optionGroupLabel="optionGroupLabel"
:optionGroupChildren="optionGroupChildren"
@option-change="onOptionChange"
/>
<span role="status" aria-live="polite" class="p-hidden-accessible"> <span role="status" aria-live="polite" class="p-hidden-accessible">
{{ selectedMessageText }} {{ selectedMessageText }}
@ -56,13 +89,34 @@ export default {
placeholder: String, placeholder: String,
disabled: Boolean, disabled: Boolean,
dataKey: null, dataKey: null,
inputId: null, inputId: {
inputStyle: null, type: String,
inputClass: null, default: null
inputProps: null, },
panelStyle: null, inputClass: {
panelClass: null, type: String,
panelProps: null, default: null
},
inputStyle: {
type: null,
default: null
},
inputProps: {
type: null,
default: null
},
panelClass: {
type: String,
default: null
},
panelStyle: {
type: null,
default: null
},
panelProps: {
type: null,
default: null
},
appendTo: { appendTo: {
type: String, type: String,
default: 'body' default: 'body'
@ -79,6 +133,10 @@ export default {
type: Boolean, type: Boolean,
default: true default: true
}, },
selectOnFocus: {
type: Boolean,
default: false
},
searchLocale: { searchLocale: {
type: String, type: String,
default: undefined default: undefined
@ -122,17 +180,15 @@ export default {
overlay: null, overlay: null,
searchTimeout: null, searchTimeout: null,
searchValue: null, searchValue: null,
selectOnFocus: false,
focusOnHover: false, focusOnHover: false,
data() { data() {
return { return {
id: UniqueComponentId(),
focused: false, focused: false,
focusedOptionInfo: { index: -1, level: 0, parentKey: '' }, focusedOptionInfo: { index: -1, level: 0, parentKey: '' },
activeOptionPath: [], activeOptionPath: [],
overlayVisible: false, overlayVisible: false,
dirty: false dirty: false
} };
}, },
watch: { watch: {
options() { options() {
@ -140,7 +196,7 @@ export default {
} }
}, },
mounted() { mounted() {
this.id = this.$attrs.id || this.id; this.autoUpdateModel();
}, },
beforeUnmount() { beforeUnmount() {
this.unbindOutsideClickListener(); this.unbindOutsideClickListener();
@ -177,6 +233,7 @@ export default {
}, },
getProccessedOptionLabel(processedOption) { getProccessedOptionLabel(processedOption) {
const grouped = this.isProccessedOptionGroup(processedOption); const grouped = this.isProccessedOptionGroup(processedOption);
return grouped ? this.getOptionGroupLabel(processedOption.option, processedOption.level) : this.getOptionLabel(processedOption.option); return grouped ? this.getOptionGroupLabel(processedOption.option, processedOption.level) : this.getOptionLabel(processedOption.option);
}, },
isProccessedOptionGroup(processedOption) { isProccessedOptionGroup(processedOption) {
@ -185,17 +242,17 @@ export default {
show(isFocus) { show(isFocus) {
this.$emit('before-show'); this.$emit('before-show');
this.overlayVisible = true; this.overlayVisible = true;
this.activeOptionPath = this.findOptionPathByValue(this.modelValue); this.activeOptionPath = this.hasSelectedOption ? this.findOptionPathByValue(this.modelValue) : this.activeOptionPath;
if (this.hasSelectedOption && ObjectUtils.isNotEmpty(this.activeOptionPath)) { if (this.hasSelectedOption && ObjectUtils.isNotEmpty(this.activeOptionPath)) {
const processedOption = this.activeOptionPath[this.activeOptionPath.length - 1]; const processedOption = this.activeOptionPath[this.activeOptionPath.length - 1];
this.focusedOptionInfo = { index: (this.autoOptionFocus ? processedOption.index : -1), level: processedOption.level, parentKey: processedOption.parentKey };
} this.focusedOptionInfo = { index: this.autoOptionFocus ? processedOption.index : -1, level: processedOption.level, parentKey: processedOption.parentKey };
else { } else {
this.focusedOptionInfo = { index: (this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1), level: 0, parentKey: '' }; this.focusedOptionInfo = { index: this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1, level: 0, parentKey: '' };
} }
isFocus && this.$refs.focusInput.focus(); isFocus && DomHandler.focus(this.$refs.focusInput);
}, },
hide(isFocus) { hide(isFocus) {
const _hide = () => { const _hide = () => {
@ -204,10 +261,12 @@ export default {
this.activeOptionPath = []; this.activeOptionPath = [];
this.focusedOptionInfo = { index: -1, level: 0, parentKey: '' }; this.focusedOptionInfo = { index: -1, level: 0, parentKey: '' };
isFocus && this.$refs.focusInput.focus(); isFocus && DomHandler.focus(this.$refs.focusInput);
} };
setTimeout(() => { _hide() }, 0); // For ScreenReaders setTimeout(() => {
_hide();
}, 0); // For ScreenReaders
}, },
onFocus(event) { onFocus(event) {
this.focused = true; this.focused = true;
@ -222,9 +281,12 @@ export default {
onKeyDown(event) { onKeyDown(event) {
if (this.disabled || this.loading) { if (this.disabled || this.loading) {
event.preventDefault(); event.preventDefault();
return; return;
} }
const metaKey = event.metaKey || event.ctrlKey;
switch (event.code) { switch (event.code) {
case 'ArrowDown': case 'ArrowDown':
this.onArrowDownKey(event); this.onArrowDownKey(event);
@ -275,7 +337,7 @@ export default {
break; break;
default: default:
if (ObjectUtils.isPrintableCharacter(event.key)) { if (!metaKey && ObjectUtils.isPrintableCharacter(event.key)) {
!this.overlayVisible && this.show(); !this.overlayVisible && this.show();
this.searchOptions(event, event.key); this.searchOptions(event, event.key);
} }
@ -284,25 +346,29 @@ export default {
} }
}, },
onOptionChange(event) { onOptionChange(event) {
const { originalEvent, processedOption, isFocus } = event; const { originalEvent, processedOption, isFocus, isHide } = event;
if (ObjectUtils.isEmpty(processedOption)) return;
const { index, level, parentKey, children } = processedOption; const { index, level, parentKey, children } = processedOption;
const grouped = ObjectUtils.isNotEmpty(children); const grouped = ObjectUtils.isNotEmpty(children);
const activeOptionPath = this.activeOptionPath.filter(p => p.parentKey !== parentKey); const activeOptionPath = this.activeOptionPath.filter((p) => p.parentKey !== parentKey);
activeOptionPath.push(processedOption); activeOptionPath.push(processedOption);
this.focusedOptionInfo = { index, level, parentKey }; this.focusedOptionInfo = { index, level, parentKey };
this.activeOptionPath = activeOptionPath; this.activeOptionPath = activeOptionPath;
grouped ? this.onOptionGroupSelect(originalEvent, processedOption) : this.onOptionSelect(originalEvent, processedOption); grouped ? this.onOptionGroupSelect(originalEvent, processedOption) : this.onOptionSelect(originalEvent, processedOption, isHide);
isFocus && this.$refs.focusInput.focus(); isFocus && DomHandler.focus(this.$refs.focusInput);
}, },
onOptionSelect(event, processedOption) { onOptionSelect(event, processedOption, isHide = true) {
const value = this.getOptionValue(processedOption.option); const value = this.getOptionValue(processedOption.option);
this.activeOptionPath.forEach(p => p.selected = true); this.activeOptionPath.forEach((p) => (p.selected = true));
this.updateModel(event, value); this.updateModel(event, value);
this.hide(true); isHide && this.hide(true);
}, },
onOptionGroupSelect(event, processedOption) { onOptionGroupSelect(event, processedOption) {
this.dirty = true; this.dirty = true;
@ -315,7 +381,7 @@ export default {
if (!this.overlay || !this.overlay.contains(event.target)) { if (!this.overlay || !this.overlay.contains(event.target)) {
this.overlayVisible ? this.hide() : this.show(); this.overlayVisible ? this.hide() : this.show();
this.$refs.focusInput.focus(); DomHandler.focus(this.$refs.focusInput);
} }
this.$emit('click', event); this.$emit('click', event);
@ -349,13 +415,13 @@ export default {
if (this.focusedOptionInfo.index !== -1) { if (this.focusedOptionInfo.index !== -1) {
const processedOption = this.visibleOptions[this.focusedOptionInfo.index]; const processedOption = this.visibleOptions[this.focusedOptionInfo.index];
const grouped = this.isProccessedOptionGroup(processedOption); const grouped = this.isProccessedOptionGroup(processedOption);
!grouped && this.onOptionChange({ originalEvent: event, processedOption }); !grouped && this.onOptionChange({ originalEvent: event, processedOption });
} }
this.overlayVisible && this.hide(); this.overlayVisible && this.hide();
event.preventDefault(); event.preventDefault();
} } else {
else {
const optionIndex = this.focusedOptionInfo.index !== -1 ? this.findPrevOptionIndex(this.focusedOptionInfo.index) : this.findLastFocusedOptionIndex(); const optionIndex = this.focusedOptionInfo.index !== -1 ? this.findPrevOptionIndex(this.focusedOptionInfo.index) : this.findLastFocusedOptionIndex();
this.changeFocusedOptionIndex(event, optionIndex); this.changeFocusedOptionIndex(event, optionIndex);
@ -367,12 +433,12 @@ export default {
onArrowLeftKey(event) { onArrowLeftKey(event) {
if (this.overlayVisible) { if (this.overlayVisible) {
const processedOption = this.visibleOptions[this.focusedOptionInfo.index]; const processedOption = this.visibleOptions[this.focusedOptionInfo.index];
const parentOption = this.activeOptionPath.find(p => p.key === processedOption.parentKey); const parentOption = this.activeOptionPath.find((p) => p.key === processedOption.parentKey);
const matched = this.focusedOptionInfo.parentKey === '' || (parentOption && parentOption.key === this.focusedOptionInfo.parentKey); const matched = this.focusedOptionInfo.parentKey === '' || (parentOption && parentOption.key === this.focusedOptionInfo.parentKey);
const root = ObjectUtils.isEmpty(processedOption.parent); const root = ObjectUtils.isEmpty(processedOption.parent);
if (matched) { if (matched) {
this.activeOptionPath = this.activeOptionPath.filter(p => p.parentKey !== this.focusedOptionInfo.parentKey); this.activeOptionPath = this.activeOptionPath.filter((p) => p.parentKey !== this.focusedOptionInfo.parentKey);
} }
if (!root) { if (!root) {
@ -390,14 +456,13 @@ export default {
const grouped = this.isProccessedOptionGroup(processedOption); const grouped = this.isProccessedOptionGroup(processedOption);
if (grouped) { if (grouped) {
const matched = this.activeOptionPath.some(p => processedOption.key === p.key); const matched = this.activeOptionPath.some((p) => processedOption.key === p.key);
if (matched) { if (matched) {
this.focusedOptionInfo = { index: -1, parentKey: processedOption.key }; this.focusedOptionInfo = { index: -1, parentKey: processedOption.key };
this.searchValue = ''; this.searchValue = '';
this.onArrowDownKey(event); this.onArrowDownKey(event);
} } else {
else {
this.onOptionChange({ originalEvent: event, processedOption }); this.onOptionChange({ originalEvent: event, processedOption });
} }
} }
@ -420,8 +485,7 @@ export default {
onEnterKey(event) { onEnterKey(event) {
if (!this.overlayVisible) { if (!this.overlayVisible) {
this.onArrowDownKey(event); this.onArrowDownKey(event);
} } else {
else {
if (this.focusedOptionInfo.index !== -1) { if (this.focusedOptionInfo.index !== -1) {
const processedOption = this.visibleOptions[this.focusedOptionInfo.index]; const processedOption = this.visibleOptions[this.focusedOptionInfo.index];
const grouped = this.isProccessedOptionGroup(processedOption); const grouped = this.isProccessedOptionGroup(processedOption);
@ -477,8 +541,7 @@ export default {
alignOverlay() { alignOverlay() {
if (this.appendTo === 'self') { if (this.appendTo === 'self') {
DomHandler.relativePosition(this.overlay, this.$el); DomHandler.relativePosition(this.overlay, this.$el);
} } else {
else {
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$el) + 'px'; this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$el) + 'px';
DomHandler.absolutePosition(this.overlay, this.$el); DomHandler.absolutePosition(this.overlay, this.$el);
} }
@ -490,6 +553,7 @@ export default {
this.hide(); this.hide();
} }
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
@ -522,6 +586,7 @@ export default {
this.hide(); this.hide();
} }
}; };
window.addEventListener('resize', this.resizeListener); window.addEventListener('resize', this.resizeListener);
} }
}, },
@ -541,31 +606,35 @@ export default {
return this.isValidOption(processedOption) && this.isSelected(processedOption); return this.isValidOption(processedOption) && this.isSelected(processedOption);
}, },
isSelected(processedOption) { isSelected(processedOption) {
return this.activeOptionPath.some(p => p.key === processedOption.key); return this.activeOptionPath.some((p) => p.key === processedOption.key);
}, },
findFirstOptionIndex() { findFirstOptionIndex() {
return this.visibleOptions.findIndex(processedOption => this.isValidOption(processedOption)); return this.visibleOptions.findIndex((processedOption) => this.isValidOption(processedOption));
}, },
findLastOptionIndex() { findLastOptionIndex() {
return ObjectUtils.findLastIndex(this.visibleOptions, processedOption => this.isValidOption(processedOption)); return ObjectUtils.findLastIndex(this.visibleOptions, (processedOption) => this.isValidOption(processedOption));
}, },
findNextOptionIndex(index) { findNextOptionIndex(index) {
const matchedOptionIndex = index < (this.visibleOptions.length - 1) ? this.visibleOptions.slice(index + 1).findIndex(processedOption => this.isValidOption(processedOption)) : -1; const matchedOptionIndex = index < this.visibleOptions.length - 1 ? this.visibleOptions.slice(index + 1).findIndex((processedOption) => this.isValidOption(processedOption)) : -1;
return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index; return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
}, },
findPrevOptionIndex(index) { findPrevOptionIndex(index) {
const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions.slice(0, index), processedOption => this.isValidOption(processedOption)) : -1; const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions.slice(0, index), (processedOption) => this.isValidOption(processedOption)) : -1;
return matchedOptionIndex > -1 ? matchedOptionIndex : index; return matchedOptionIndex > -1 ? matchedOptionIndex : index;
}, },
findSelectedOptionIndex() { findSelectedOptionIndex() {
return this.visibleOptions.findIndex(processedOption => this.isValidSelectedOption(processedOption)); return this.visibleOptions.findIndex((processedOption) => this.isValidSelectedOption(processedOption));
}, },
findFirstFocusedOptionIndex() { findFirstFocusedOptionIndex() {
const selectedIndex = this.findSelectedOptionIndex(); const selectedIndex = this.findSelectedOptionIndex();
return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex; return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
}, },
findLastFocusedOptionIndex() { findLastFocusedOptionIndex() {
const selectedIndex = this.findSelectedOptionIndex(); const selectedIndex = this.findSelectedOptionIndex();
return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex; return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
}, },
findOptionPathByValue(value, processedOptions, level = 0) { findOptionPathByValue(value, processedOptions, level = 0) {
@ -582,6 +651,7 @@ export default {
} }
const matchedOptions = this.findOptionPathByValue(value, processedOption.children, level + 1); const matchedOptions = this.findOptionPathByValue(value, processedOption.children, level + 1);
if (matchedOptions) { if (matchedOptions) {
matchedOptions.unshift(processedOption); matchedOptions.unshift(processedOption);
@ -596,11 +666,10 @@ export default {
let matched = false; let matched = false;
if (this.focusedOptionInfo.index !== -1) { if (this.focusedOptionInfo.index !== -1) {
optionIndex = this.visibleOptions.slice(this.focusedOptionInfo.index).findIndex(processedOption => this.isOptionMatched(processedOption)); optionIndex = this.visibleOptions.slice(this.focusedOptionInfo.index).findIndex((processedOption) => this.isOptionMatched(processedOption));
optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionInfo.index).findIndex(processedOption => this.isOptionMatched(processedOption)) : optionIndex + this.focusedOptionInfo.index; optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionInfo.index).findIndex((processedOption) => this.isOptionMatched(processedOption)) : optionIndex + this.focusedOptionInfo.index;
} } else {
else { optionIndex = this.visibleOptions.findIndex((processedOption) => this.isOptionMatched(processedOption));
optionIndex = this.visibleOptions.findIndex(processedOption => this.isOptionMatched(processedOption));
} }
if (optionIndex !== -1) { if (optionIndex !== -1) {
@ -632,13 +701,14 @@ export default {
this.scrollInView(); this.scrollInView();
if (this.selectOnFocus) { if (this.selectOnFocus) {
this.updateModel(event, this.getOptionValue(this.visibleOptions[index])); this.onOptionChange({ originalEvent: event, processedOption: this.visibleOptions[index], isHide: false });
} }
} }
}, },
scrollInView(index = -1) { scrollInView(index = -1) {
const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId; const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
const element = DomHandler.findSingle(this.list, `li[id="${id}"]`); const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
if (element) { if (element) {
element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' }); element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
} }
@ -646,8 +716,9 @@ export default {
autoUpdateModel() { autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) { if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
this.focusedOptionInfo.index = this.findFirstFocusedOptionIndex(); this.focusedOptionInfo.index = this.findFirstFocusedOptionIndex();
const value = this.getOptionValue(this.visibleOptions[this.focusedOptionInfo.index]); this.onOptionChange({ processedOption: this.visibleOptions[this.focusedOptionInfo.index], isHide: false });
this.updateModel(null, value);
!this.overlayVisible && (this.focusedOptionInfo = { index: -1, level: 0, parentKey: '' });
} }
}, },
updateModel(event, value) { updateModel(event, value) {
@ -657,7 +728,8 @@ export default {
createProcessedOptions(options, level = 0, parent = {}, parentKey = '') { createProcessedOptions(options, level = 0, parent = {}, parentKey = '') {
const processedOptions = []; const processedOptions = [];
options && options.forEach((option, index) => { options &&
options.forEach((option, index) => {
const key = (parentKey !== '' ? parentKey + '_' : '') + index; const key = (parentKey !== '' ? parentKey + '_' : '') + index;
const newOption = { const newOption = {
option, option,
@ -666,7 +738,7 @@ export default {
key, key,
parent, parent,
parentKey parentKey
} };
newOption['children'] = this.createProcessedOptions(this.getOptionGroupChildren(option, level), level + 1, newOption, key); newOption['children'] = this.createProcessedOptions(this.getOptionGroupChildren(option, level), level + 1, newOption, key);
processedOptions.push(newOption); processedOptions.push(newOption);
@ -680,25 +752,35 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-cascadeselect p-component p-inputwrapper', { return [
'p-cascadeselect p-component p-inputwrapper',
{
'p-disabled': this.disabled, 'p-disabled': this.disabled,
'p-focus': this.focused, 'p-focus': this.focused,
'p-inputwrapper-filled': this.modelValue, 'p-inputwrapper-filled': this.modelValue,
'p-inputwrapper-focus': this.focused || this.overlayVisible, 'p-inputwrapper-focus': this.focused || this.overlayVisible,
'p-overlay-open': this.overlayVisible 'p-overlay-open': this.overlayVisible
}]; }
];
}, },
labelClass() { labelClass() {
return ['p-cascadeselect-label', { return [
'p-cascadeselect-label',
{
'p-placeholder': this.label === this.placeholder, 'p-placeholder': this.label === this.placeholder,
'p-cascadeselect-label-empty': !this.$slots['value'] && (this.label === 'p-emptylabel' || this.label.length === 0) 'p-cascadeselect-label-empty': !this.$slots['value'] && (this.label === 'p-emptylabel' || this.label.length === 0)
}]; }
];
}, },
panelStyleClass() { panelStyleClass() {
return ['p-cascadeselect-panel p-component', this.panelClass, { return [
'p-cascadeselect-panel p-component',
this.panelClass,
{
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}]; }
];
}, },
dropdownIconClass() { dropdownIconClass() {
return ['p-cascadeselect-trigger-icon', this.loading ? this.loadingIcon : 'pi pi-chevron-down']; return ['p-cascadeselect-trigger-icon', this.loading ? this.loadingIcon : 'pi pi-chevron-down'];
@ -711,7 +793,7 @@ export default {
if (this.hasSelectedOption) { if (this.hasSelectedOption) {
const activeOptionPath = this.findOptionPathByValue(this.modelValue); const activeOptionPath = this.findOptionPathByValue(this.modelValue);
const processedOption = activeOptionPath.length ? activeOptionPath[activeOptionPath.length - 1] : null; const processedOption = ObjectUtils.isNotEmpty(activeOptionPath) ? activeOptionPath[activeOptionPath.length - 1] : null;
return processedOption ? this.getOptionLabel(processedOption.option) : label; return processedOption ? this.getOptionLabel(processedOption.option) : label;
} }
@ -722,7 +804,8 @@ export default {
return this.createProcessedOptions(this.options || []); return this.createProcessedOptions(this.options || []);
}, },
visibleOptions() { visibleOptions() {
const processedOption = this.activeOptionPath.find(p => p.key === this.focusedOptionInfo.parentKey); const processedOption = this.activeOptionPath.find((p) => p.key === this.focusedOptionInfo.parentKey);
return processedOption ? processedOption.children : this.processedOptions; return processedOption ? processedOption.children : this.processedOptions;
}, },
equalityKey() { equalityKey() {
@ -749,15 +832,18 @@ export default {
selectedMessageText() { selectedMessageText() {
return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', '1') : this.emptySelectionMessageText; return this.hasSelectedOption ? this.selectionMessageText.replaceAll('{0}', '1') : this.emptySelectionMessageText;
}, },
id() {
return this.$attrs.id || UniqueComponentId();
},
focusedOptionId() { focusedOptionId() {
return this.focusedOptionInfo.index !== -1 ? `${this.id}${ObjectUtils.isNotEmpty(this.focusedOptionInfo.parentKey) ? '_' + this.focusedOptionInfo.parentKey : ''}_${this.focusedOptionInfo.index}` : null; return this.focusedOptionInfo.index !== -1 ? `${this.id}${ObjectUtils.isNotEmpty(this.focusedOptionInfo.parentKey) ? '_' + this.focusedOptionInfo.parentKey : ''}_${this.focusedOptionInfo.index}` : null;
} }
}, },
components: { components: {
'CascadeSelectSub': CascadeSelectSub, CascadeSelectSub: CascadeSelectSub,
'Portal': Portal Portal: Portal
}
} }
};
</script> </script>
<style> <style>

View file

@ -1,17 +1,47 @@
<template> <template>
<ul class="p-cascadeselect-panel p-cascadeselect-items"> <ul class="p-cascadeselect-panel p-cascadeselect-items">
<template v-for="(processedOption, index) of options" :key="getOptionLabelToRender(processedOption)"> <template v-for="(processedOption, index) of options" :key="getOptionLabelToRender(processedOption)">
<li :id="getOptionId(processedOption)" :class="['p-cascadeselect-item', {'p-cascadeselect-item-group': isOptionGroup(processedOption), 'p-cascadeselect-item-active p-highlight': isOptionActive(processedOption), 'p-focus': isOptionFocused(processedOption), 'p-disabled': isOptionDisabled(processedOption)}]" <li
role="treeitem" :aria-label="getOptionLabelToRender(processedOption)" :aria-selected="isOptionGroup(processedOption) ? undefined : isOptionSelected(processedOption)" :aria-expanded="isOptionGroup(processedOption) ? isOptionActive(processedOption) : undefined" :id="getOptionId(processedOption)"
:aria-setsize="processedOption.length" :aria-posinset="index + 1" :aria-level="level + 1"> :class="[
<div class="p-cascadeselect-item-content" @click="onOptionClick($event, processedOption)" v-ripple> 'p-cascadeselect-item',
{
'p-cascadeselect-item-group': isOptionGroup(processedOption),
'p-cascadeselect-item-active p-highlight': isOptionActive(processedOption),
'p-focus': isOptionFocused(processedOption),
'p-disabled': isOptionDisabled(processedOption)
}
]"
role="treeitem"
:aria-label="getOptionLabelToRender(processedOption)"
:aria-selected="isOptionGroup(processedOption) ? undefined : isOptionSelected(processedOption)"
:aria-expanded="isOptionGroup(processedOption) ? isOptionActive(processedOption) : undefined"
:aria-setsize="processedOption.length"
:aria-posinset="index + 1"
:aria-level="level + 1"
>
<div v-ripple class="p-cascadeselect-item-content" @click="onOptionClick($event, processedOption)">
<component v-if="templates['option']" :is="templates['option']" :option="processedOption.option" /> <component v-if="templates['option']" :is="templates['option']" :option="processedOption.option" />
<span v-else class="p-cascadeselect-item-text">{{ getOptionLabelToRender(processedOption) }}</span> <span v-else class="p-cascadeselect-item-text">{{ getOptionLabelToRender(processedOption) }}</span>
<span v-if="isOptionGroup(processedOption)" class="p-cascadeselect-group-icon pi pi-angle-right" aria-hidden="true"></span> <span v-if="isOptionGroup(processedOption)" class="p-cascadeselect-group-icon pi pi-angle-right" aria-hidden="true"></span>
</div> </div>
<CascadeSelectSub v-if="isOptionGroup(processedOption) && isOptionActive(processedOption)" role="group" class="p-cascadeselect-sublist" :selectId="selectId" :focusedOptionId="focusedOptionId" <CascadeSelectSub
:options="getOptionGroupChildren(processedOption)" :activeOptionPath="activeOptionPath" :level="level + 1" :templates="templates" :optionLabel="optionLabel" :optionValue="optionValue" :optionDisabled="optionDisabled" v-if="isOptionGroup(processedOption) && isOptionActive(processedOption)"
:optionGroupLabel="optionGroupLabel" :optionGroupChildren="optionGroupChildren" @option-change="onOptionChange" /> role="group"
class="p-cascadeselect-sublist"
:selectId="selectId"
:focusedOptionId="focusedOptionId"
:options="getOptionGroupChildren(processedOption)"
:activeOptionPath="activeOptionPath"
:level="level + 1"
:templates="templates"
:optionLabel="optionLabel"
:optionValue="optionValue"
:optionDisabled="optionDisabled"
:optionGroupLabel="optionGroupLabel"
:optionGroupChildren="optionGroupChildren"
@option-change="onOptionChange"
/>
</li> </li>
</template> </template>
</ul> </ul>
@ -68,7 +98,7 @@ export default {
return !this.isOptionGroup(processedOption) && this.isOptionActive(processedOption); return !this.isOptionGroup(processedOption) && this.isOptionActive(processedOption);
}, },
isOptionActive(processedOption) { isOptionActive(processedOption) {
return this.activeOptionPath.some(path => path.key === processedOption.key); return this.activeOptionPath.some((path) => path.key === processedOption.key);
}, },
isOptionFocused(processedOption) { isOptionFocused(processedOption) {
return this.focusedOptionId === this.getOptionId(processedOption); return this.focusedOptionId === this.getOptionId(processedOption);
@ -89,13 +119,13 @@ export default {
const sublistWidth = this.$el.offsetParent ? this.$el.offsetWidth : DomHandler.getHiddenElementOuterWidth(this.$el); const sublistWidth = this.$el.offsetParent ? this.$el.offsetWidth : DomHandler.getHiddenElementOuterWidth(this.$el);
const itemOuterWidth = DomHandler.getOuterWidth(parentItem.children[0]); const itemOuterWidth = DomHandler.getOuterWidth(parentItem.children[0]);
if ((parseInt(containerOffset.left, 10) + itemOuterWidth + sublistWidth) > (viewport.width - DomHandler.calculateScrollbarWidth())) { if (parseInt(containerOffset.left, 10) + itemOuterWidth + sublistWidth > viewport.width - DomHandler.calculateScrollbarWidth()) {
this.$el.style.left = '-100%'; this.$el.style.left = '-100%';
} }
} }
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}
} }
};
</script> </script>

View file

@ -44,21 +44,20 @@ export interface ChartProps {
height?: number | undefined; height?: number | undefined;
} }
export interface ChartSlots { export interface ChartSlots {}
}
export declare type ChartEmits = { export declare type ChartEmits = {
/** /**
* Callback to invoke when a tab gets expanded. * Callback to invoke when a tab gets expanded.
* @param {ChartSelectEvent} event - Custom select event. * @param {ChartSelectEvent} event - Custom select event.
*/ */
'select': (event: ChartSelectEvent) => void; select: (event: ChartSelectEvent) => void;
/** /**
* Callback to invoke when chart is loaded. * Callback to invoke when chart is loaded.
* @param {*} chart - Chart instance. * @param {*} chart - Chart instance.
*/ */
'loaded': (chart: any) => void; loaded: (chart: any) => void;
} };
declare class Chart extends ClassComponent<ChartProps, ChartSlots, ChartEmits> { declare class Chart extends ClassComponent<ChartProps, ChartSlots, ChartEmits> {
/** /**
@ -89,7 +88,7 @@ declare class Chart extends ClassComponent<ChartProps, ChartSlots, ChartEmits> {
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Chart: GlobalComponentConstructor<Chart> Chart: GlobalComponentConstructor<Chart>;
} }
} }
@ -103,7 +102,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Chart](https://www.primefaces.org/primevue/showcase/#/chart) * - [Chart](https://www.primefaces.org/primevue/chart)
* *
*/ */
export default Chart; export default Chart;

View file

@ -20,18 +20,9 @@ export default {
height: { height: {
type: Number, type: Number,
default: 150 default: 150
},
},
chart: null,
mounted() {
this.initChart();
},
beforeUnmount() {
if (this.chart) {
this.chart.destroy();
this.chart = null;
} }
}, },
chart: null,
watch: { watch: {
/* /*
* Use deep watch to enable triggering watch for changes within structure * Use deep watch to enable triggering watch for changes within structure
@ -50,6 +41,15 @@ export default {
this.reinit(); this.reinit();
} }
}, },
mounted() {
this.initChart();
},
beforeUnmount() {
if (this.chart) {
this.chart.destroy();
this.chart = null;
}
},
methods: { methods: {
initChart() { initChart() {
import('chart.js/auto').then((module) => { import('chart.js/auto').then((module) => {
@ -103,7 +103,7 @@ export default {
} }
} }
} }
} };
</script> </script>
<style> <style>

View file

@ -68,8 +68,7 @@ export interface CheckboxProps {
'aria-label'?: string | undefined; 'aria-label'?: string | undefined;
} }
export interface CheckboxSlots { export interface CheckboxSlots {}
}
export declare type CheckboxEmits = { export declare type CheckboxEmits = {
/** /**
@ -81,24 +80,24 @@ export declare type CheckboxEmits = {
* Callback to invoke on value click. * Callback to invoke on value click.
* @param {MouseEvent} event - Browser event. * @param {MouseEvent} event - Browser event.
*/ */
'click': (event: MouseEvent) => void; click: (event: MouseEvent) => void;
/** /**
* Callback to invoke on value change. * Callback to invoke on value change.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'change': (event: Event) => void; change: (event: Event) => void;
/** /**
* Callback to invoke on value change. * Callback to invoke on value change.
* @param {boolean} value - New value. * @param {boolean} value - New value.
*/ */
'input': (value: boolean) => void; input: (value: boolean) => void;
} };
declare class Checkbox extends ClassComponent<CheckboxProps, CheckboxSlots, CheckboxEmits> {} declare class Checkbox extends ClassComponent<CheckboxProps, CheckboxSlots, CheckboxEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Checkbox: GlobalComponentConstructor<Checkbox> Checkbox: GlobalComponentConstructor<Checkbox>;
} }
} }
@ -108,7 +107,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Checkbox](https://www.primefaces.org/primevue/showcase/#/checkbox) * - [Checkbox](https://www.primefaces.org/primevue/checkbox)
* *
*/ */
export default Checkbox; export default Checkbox;

View file

@ -1,8 +1,25 @@
<template> <template>
<div :class="containerClass" @click="onClick($event)"> <div :class="containerClass" @click="onClick($event)">
<div class="p-hidden-accessible"> <div class="p-hidden-accessible">
<input :id="inputId" ref="input" type="checkbox" :value="value" :class="inputClass" :style="inputStyle" :name="name" :checked="checked" :tabindex="tabindex" :disabled="disabled" :readonly="readonly" :required="required" :aria-labelledby="ariaLabelledby" :aria-label="ariaLabel" <input
@focus="onFocus($event)" @blur="onBlur($event)" v-bind="inputProps"> ref="input"
:id="inputId"
type="checkbox"
:value="value"
:class="inputClass"
:style="inputStyle"
:name="name"
:checked="checked"
:tabindex="tabindex"
:disabled="disabled"
:readonly="readonly"
:required="required"
:aria-labelledby="ariaLabelledby"
:aria-label="ariaLabel"
@focus="onFocus($event)"
@blur="onBlur($event)"
v-bind="inputProps"
/>
</div> </div>
<div ref="box" :class="['p-checkbox-box', { 'p-highlight': checked, 'p-disabled': disabled, 'p-focus': focused }]"> <div ref="box" :class="['p-checkbox-box', { 'p-highlight': checked, 'p-disabled': disabled, 'p-focus': focused }]">
<span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span> <span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span>
@ -48,10 +65,22 @@ export default {
type: Number, type: Number,
default: null default: null
}, },
inputId: null, inputId: {
inputClass: null, type: String,
inputStyle: null, default: null
inputProps: null, },
inputClass: {
type: String,
default: null
},
inputStyle: {
type: null,
default: null
},
inputProps: {
type: null,
default: null
},
'aria-labelledby': { 'aria-labelledby': {
type: String, type: String,
default: null default: null
@ -64,7 +93,7 @@ export default {
data() { data() {
return { return {
focused: false focused: false
} };
}, },
methods: { methods: {
onClick(event) { onClick(event) {
@ -73,12 +102,9 @@ export default {
if (this.binary) { if (this.binary) {
newModelValue = this.checked ? this.falseValue : this.trueValue; newModelValue = this.checked ? this.falseValue : this.trueValue;
} } else {
else { if (this.checked) newModelValue = this.modelValue.filter((val) => !ObjectUtils.equals(val, this.value));
if (this.checked) else newModelValue = this.modelValue ? [...this.modelValue, this.value] : [this.value];
newModelValue = this.modelValue.filter(val => !ObjectUtils.equals(val, this.value));
else
newModelValue = this.modelValue ? [...this.modelValue, this.value] : [this.value];
} }
this.$emit('click', event); this.$emit('click', event);
@ -103,12 +129,14 @@ export default {
}, },
containerClass() { containerClass() {
return [ return [
'p-checkbox p-component', { 'p-checkbox p-component',
{
'p-checkbox-checked': this.checked, 'p-checkbox-checked': this.checked,
'p-checkbox-disabled': this.disabled, 'p-checkbox-disabled': this.disabled,
'p-checkbox-focused': this.focused 'p-checkbox-focused': this.focused
}]; }
} ];
} }
} }
};
</script> </script>

View file

@ -37,14 +37,14 @@ export declare type ChipEmits = {
* Callback to invoke when a chip is removed. * Callback to invoke when a chip is removed.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'remove': (event: Event) => void; remove: (event: Event) => void;
} };
declare class Chip extends ClassComponent<ChipProps, ChipSlots, ChipEmits> {} declare class Chip extends ClassComponent<ChipProps, ChipSlots, ChipEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Chip: GlobalComponentConstructor<Chip> Chip: GlobalComponentConstructor<Chip>;
} }
} }
@ -54,7 +54,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Chip](https://www.primefaces.org/primevue/showcase/#/chip) * - [Chip](https://www.primefaces.org/primevue/chip)
* *
*/ */
export default Chip; export default Chip;

View file

@ -22,7 +22,7 @@ describe('Chip.vue', () => {
}); });
it('should close icon work', async () => { it('should close icon work', async () => {
await wrapper.find('.p-chip-remove-icon').trigger('click') await wrapper.find('.p-chip-remove-icon').trigger('click');
expect(wrapper.find('.p-chip.p-component').exists()).toBe(false); expect(wrapper.find('.p-chip.p-component').exists()).toBe(false);
}); });

View file

@ -1,12 +1,11 @@
<template> <template>
<div :class="containerClass" v-if="visible"> <div v-if="visible" :class="containerClass">
<slot> <slot>
<img :src="image" v-if="image"> <img v-if="image" :src="image" />
<span :class="iconClass" v-else-if="icon"></span> <span v-else-if="icon" :class="iconClass"></span>
<div class="p-chip-text" v-if="label">{{label}}</div> <div v-if="label" class="p-chip-text">{{ label }}</div>
</slot> </slot>
<span v-if="removable" tabindex="0" :class="removeIconClass" <span v-if="removable" tabindex="0" :class="removeIconClass" @click="close" @keydown.enter="close"></span>
@click="close" @keydown.enter="close"></span>
</div> </div>
</template> </template>
@ -39,7 +38,7 @@ export default {
data() { data() {
return { return {
visible: true visible: true
} };
}, },
methods: { methods: {
close(event) { close(event) {
@ -49,9 +48,12 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-chip p-component', { return [
'p-chip p-component',
{
'p-chip-image': this.image != null 'p-chip-image': this.image != null
}]; }
];
}, },
iconClass() { iconClass() {
return ['p-chip-icon', this.icon]; return ['p-chip-icon', this.icon];
@ -60,7 +62,7 @@ export default {
return ['p-chip-remove-icon', this.removeIcon]; return ['p-chip-remove-icon', this.removeIcon];
} }
} }
} };
</script> </script>
<style> <style>

View file

@ -96,19 +96,19 @@ export declare type ChipsEmits = {
* Callback to invoke when a chip is added. * Callback to invoke when a chip is added.
* @param {ChipsAddEvent} event - Custom add event. * @param {ChipsAddEvent} event - Custom add event.
*/ */
'add': (event: ChipsAddEvent) => void; add: (event: ChipsAddEvent) => void;
/** /**
* Callback to invoke when a chip is removed. * Callback to invoke when a chip is removed.
* @param {ChipsRemoveEvent} event - Custom remove event. * @param {ChipsRemoveEvent} event - Custom remove event.
*/ */
'remove': (event: ChipsRemoveEvent) => void; remove: (event: ChipsRemoveEvent) => void;
} };
declare class Chips extends ClassComponent<ChipsProps, ChipsSlots, ChipsEmits> {} declare class Chips extends ClassComponent<ChipsProps, ChipsSlots, ChipsEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Chips: GlobalComponentConstructor<Chips> Chips: GlobalComponentConstructor<Chips>;
} }
} }
@ -118,7 +118,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Chips](https://www.primefaces.org/primevue/showcase/#/chips) * - [Chips](https://www.primefaces.org/primevue/chips)
* *
*/ */
export default Chips; export default Chips;

View file

@ -1,17 +1,51 @@
<template> <template>
<div :class="containerClass"> <div :class="containerClass">
<ul ref="container" class="p-inputtext p-chips-multiple-container" tabindex="-1" role="listbox" aria-orientation="horizontal" :aria-labelledby="ariaLabelledby" :aria-label="ariaLabel" :aria-activedescendant="focused ? focusedOptionId : undefined" <ul
@click="onWrapperClick()" @focus="onContainerFocus" @blur="onContainerBlur" @keydown="onContainerKeyDown"> ref="container"
<li v-for="(val,i) of modelValue" :key="`${i}_${val}`" :id="id + '_chips_item_' + i" role="option" :class="['p-chips-token', {'p-focus': focusedIndex === i}]" class="p-inputtext p-chips-multiple-container"
:aria-label="val" :aria-selected="true" :aria-setsize="modelValue.length" :aria-posinset="i + 1"> tabindex="-1"
role="listbox"
aria-orientation="horizontal"
:aria-labelledby="ariaLabelledby"
:aria-label="ariaLabel"
:aria-activedescendant="focused ? focusedOptionId : undefined"
@click="onWrapperClick()"
@focus="onContainerFocus"
@blur="onContainerBlur"
@keydown="onContainerKeyDown"
>
<li
v-for="(val, i) of modelValue"
:key="`${i}_${val}`"
:id="id + '_chips_item_' + i"
role="option"
:class="['p-chips-token', { 'p-focus': focusedIndex === i }]"
:aria-label="val"
:aria-selected="true"
:aria-setsize="modelValue.length"
:aria-posinset="i + 1"
>
<slot name="chip" :value="val"> <slot name="chip" :value="val">
<span class="p-chips-token-label">{{ val }}</span> <span class="p-chips-token-label">{{ val }}</span>
</slot> </slot>
<span class="p-chips-token-icon pi pi-times-circle" @click="removeItem($event, i)" aria-hidden="true"></span> <span class="p-chips-token-icon pi pi-times-circle" @click="removeItem($event, i)" aria-hidden="true"></span>
</li> </li>
<li class="p-chips-input-token" role="option"> <li class="p-chips-input-token" role="option">
<input ref="input" type="text" :id="inputId" :class="inputClass" :style="inputStyle" :disabled="disabled || maxedOut" :placeholder="placeholder" <input
@focus="onFocus($event)" @blur="onBlur($event)" @input="onInput" @keydown="onKeyDown($event)" @paste="onPaste($event)" v-bind="inputProps"> ref="input"
:id="inputId"
type="text"
:class="inputClass"
:style="inputStyle"
:disabled="disabled || maxedOut"
:placeholder="placeholder"
@focus="onFocus($event)"
@blur="onBlur($event)"
@input="onInput"
@keydown="onKeyDown($event)"
@paste="onPaste($event)"
v-bind="inputProps"
/>
</li> </li>
</ul> </ul>
</div> </div>
@ -48,14 +82,26 @@ export default {
type: String, type: String,
default: null default: null
}, },
inputId: null,
inputClass: null,
inputStyle: null,
inputProps: null,
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false default: false
}, },
inputId: {
type: String,
default: null
},
inputClass: {
type: String,
default: null
},
inputStyle: {
type: null,
default: null
},
inputProps: {
type: null,
default: null
},
'aria-labelledby': { 'aria-labelledby': {
type: String, type: String,
default: null default: null
@ -89,9 +135,11 @@ export default {
onBlur(event) { onBlur(event) {
this.focused = false; this.focused = false;
this.focusedIndex = null; this.focusedIndex = null;
if (this.addOnBlur) { if (this.addOnBlur) {
this.addItem(event, event.target.value, false); this.addItem(event, event.target.value, false);
} }
this.$emit('blur', event); this.$emit('blur', event);
}, },
onKeyDown(event) { onKeyDown(event) {
@ -102,21 +150,23 @@ export default {
if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) { if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
if (this.focusedIndex !== null) { if (this.focusedIndex !== null) {
this.removeItem(event, this.focusedIndex); this.removeItem(event, this.focusedIndex);
} else this.removeItem(event, this.modelValue.length - 1);
} }
else this.removeItem(event, this.modelValue.length - 1);
}
break; break;
case 'Enter': case 'Enter':
if (inputValue && inputValue.trim().length && !this.maxedOut) { if (inputValue && inputValue.trim().length && !this.maxedOut) {
this.addItem(event, inputValue, true); this.addItem(event, inputValue, true);
} }
break; break;
case 'ArrowLeft': case 'ArrowLeft':
if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) { if (inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
this.$refs.container.focus(); this.$refs.container.focus();
} }
break; break;
case 'ArrowRight': case 'ArrowRight':
@ -129,16 +179,19 @@ export default {
this.addItem(event, inputValue, true); this.addItem(event, inputValue, true);
} }
} }
break; break;
} }
}, },
onPaste(event) { onPaste(event) {
if (this.separator) { if (this.separator) {
let pastedData = (event.clipboardData || window['clipboardData']).getData('Text'); let pastedData = (event.clipboardData || window['clipboardData']).getData('Text');
if (pastedData) { if (pastedData) {
let value = this.modelValue || []; let value = this.modelValue || [];
let pastedValues = pastedData.split(this.separator); let pastedValues = pastedData.split(this.separator);
pastedValues = pastedValues.filter(val => (this.allowDuplicate || value.indexOf(val) === -1));
pastedValues = pastedValues.filter((val) => this.allowDuplicate || value.indexOf(val) === -1);
value = [...value, ...pastedValues]; value = [...value, ...pastedValues];
this.updateModel(event, value, true); this.updateModel(event, value, true);
} }
@ -177,12 +230,10 @@ export default {
}, },
onArrowRightKeyOn() { onArrowRightKeyOn() {
if (this.inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) { if (this.inputValue.length === 0 && this.modelValue && this.modelValue.length > 0) {
if (this.focusedIndex === this.modelValue.length - 1) { if (this.focusedIndex === this.modelValue.length - 1) {
this.focusedIndex = null; this.focusedIndex = null;
this.$refs.input.focus(); this.$refs.input.focus();
} } else {
else {
this.focusedIndex++; this.focusedIndex++;
} }
} }
@ -208,6 +259,7 @@ export default {
addItem(event, item, preventDefault) { addItem(event, item, preventDefault) {
if (item && item.trim().length) { if (item && item.trim().length) {
let value = this.modelValue ? [...this.modelValue] : []; let value = this.modelValue ? [...this.modelValue] : [];
if (this.allowDuplicate || value.indexOf(item) === -1) { if (this.allowDuplicate || value.indexOf(item) === -1) {
value.push(item); value.push(item);
this.updateModel(event, value, preventDefault); this.updateModel(event, value, preventDefault);
@ -221,6 +273,7 @@ export default {
let values = [...this.modelValue]; let values = [...this.modelValue];
const removedItem = values.splice(index, 1); const removedItem = values.splice(index, 1);
this.focusedIndex = null; this.focusedIndex = null;
this.$refs.input.focus(); this.$refs.input.focus();
this.$emit('update:modelValue', values); this.$emit('update:modelValue', values);
@ -235,18 +288,21 @@ export default {
return this.max && this.modelValue && this.max === this.modelValue.length; return this.max && this.modelValue && this.max === this.modelValue.length;
}, },
containerClass() { containerClass() {
return ['p-chips p-component p-inputwrapper', { return [
'p-chips p-component p-inputwrapper',
{
'p-disabled': this.disabled, 'p-disabled': this.disabled,
'p-focus': this.focused, 'p-focus': this.focused,
'p-inputwrapper-filled': ((this.modelValue && this.modelValue.length) || (this.inputValue && this.inputValue.length)), 'p-inputwrapper-filled': (this.modelValue && this.modelValue.length) || (this.inputValue && this.inputValue.length),
'p-inputwrapper-focus': this.focused 'p-inputwrapper-focus': this.focused
}]; }
];
}, },
focusedOptionId() { focusedOptionId() {
return this.focusedIndex !== null ? `${this.id}_chips_item_${this.focusedIndex}` : null; return this.focusedIndex !== null ? `${this.id}_chips_item_${this.focusedIndex}` : null;
} }
} }
} };
</script> </script>
<style> <style>

View file

@ -65,8 +65,7 @@ export interface ColorPickerProps {
appendTo?: ColorPickerAppendToType; appendTo?: ColorPickerAppendToType;
} }
export interface ColorPickerSlots { export interface ColorPickerSlots {}
}
export declare type ColorPickerEmits = { export declare type ColorPickerEmits = {
/** /**
@ -78,22 +77,22 @@ export declare type ColorPickerEmits = {
* Callback to invoke when a chip is added. * Callback to invoke when a chip is added.
* @param {ColorPickerChangeEvent} event - Custom add event. * @param {ColorPickerChangeEvent} event - Custom add event.
*/ */
'change': (event: ColorPickerChangeEvent) => void; change: (event: ColorPickerChangeEvent) => void;
/** /**
* Callback to invoke when input is cleared by the user. * Callback to invoke when input is cleared by the user.
*/ */
'show': () => void; show: () => void;
/** /**
* Callback to invoke when input is cleared by the user. * Callback to invoke when input is cleared by the user.
*/ */
'hide': () => void; hide: () => void;
} };
declare class ColorPicker extends ClassComponent<ColorPickerProps, ColorPickerSlots, ColorPickerEmits> {} declare class ColorPicker extends ClassComponent<ColorPickerProps, ColorPickerSlots, ColorPickerEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
ColorPicker: GlobalComponentConstructor<ColorPicker> ColorPicker: GlobalComponentConstructor<ColorPicker>;
} }
} }
@ -103,7 +102,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [ColorPicker](https://www.primefaces.org/primevue/showcase/#/colorpicker) * - [ColorPicker](https://www.primefaces.org/primevue/colorpicker)
* *
*/ */
export default ColorPicker; export default ColorPicker;

View file

@ -1,19 +1,16 @@
<template> <template>
<div ref="container" :class="containerClass"> <div ref="container" :class="containerClass">
<input ref="input" type="text" :class="inputClass" readonly="readonly" :tabindex="tabindex" :disabled="disabled" <input v-if="!inline" ref="input" type="text" :class="inputClass" readonly="readonly" :tabindex="tabindex" :disabled="disabled" @click="onInputClick" @keydown="onInputKeydown" />
@click="onInputClick" @keydown="onInputKeydown" v-if="!inline"/>
<Portal :appendTo="appendTo" :disabled="inline"> <Portal :appendTo="appendTo" :disabled="inline">
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave"> <transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave">
<div :ref="pickerRef" :class="pickerClass" v-if="inline ? true : overlayVisible" @click="onOverlayClick"> <div v-if="inline ? true : overlayVisible" :ref="pickerRef" :class="pickerClass" @click="onOverlayClick">
<div class="p-colorpicker-content"> <div class="p-colorpicker-content">
<div :ref="colorSelectorRef" class="p-colorpicker-color-selector" @mousedown="onColorMousedown($event)" <div :ref="colorSelectorRef" class="p-colorpicker-color-selector" @mousedown="onColorMousedown($event)" @touchstart="onColorDragStart($event)" @touchmove="onDrag($event)" @touchend="onDragEnd()">
@touchstart="onColorDragStart($event)" @touchmove="onDrag($event)" @touchend="onDragEnd()">
<div class="p-colorpicker-color"> <div class="p-colorpicker-color">
<div :ref="colorHandleRef" class="p-colorpicker-color-handle"></div> <div :ref="colorHandleRef" class="p-colorpicker-color-handle"></div>
</div> </div>
</div> </div>
<div :ref="hueViewRef" class="p-colorpicker-hue" @mousedown="onHueMousedown($event)" <div :ref="hueViewRef" class="p-colorpicker-hue" @mousedown="onHueMousedown($event)" @touchstart="onHueDragStart($event)" @touchmove="onDrag($event)" @touchend="onDragEnd()">
@touchstart="onHueDragStart($event)" @touchmove="onDrag($event)" @touchend="onDragEnd()">
<div :ref="hueHandleRef" class="p-colorpicker-hue-handle"></div> <div :ref="hueHandleRef" class="p-colorpicker-hue-handle"></div>
</div> </div>
</div> </div>
@ -89,6 +86,17 @@ export default {
colorHandle: null, colorHandle: null,
hueView: null, hueView: null,
hueHandle: null, hueHandle: null,
watch: {
modelValue: {
immediate: true,
handler(newValue) {
this.hsbValue = this.toHSB(newValue);
if (this.selfUpdate) this.selfUpdate = false;
else this.updateUI();
}
}
},
beforeUnmount() { beforeUnmount() {
this.unbindOutsideClickListener(); this.unbindOutsideClickListener();
this.unbindDragListeners(); this.unbindDragListeners();
@ -108,26 +116,14 @@ export default {
mounted() { mounted() {
this.updateUI(); this.updateUI();
}, },
watch: {
modelValue: {
immediate: true,
handler(newValue) {
this.hsbValue = this.toHSB(newValue);
if (this.selfUpdate)
this.selfUpdate = false;
else
this.updateUI();
}
}
},
methods: { methods: {
pickColor(event) { pickColor(event) {
let rect = this.colorSelector.getBoundingClientRect(); let rect = this.colorSelector.getBoundingClientRect();
let top = rect.top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0); let top = rect.top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
let left = rect.left + document.body.scrollLeft; let left = rect.left + document.body.scrollLeft;
let saturation = Math.floor(100 * (Math.max(0, Math.min(150, ((event.pageX || event.changedTouches[0].pageX)- left)))) / 150); let saturation = Math.floor((100 * Math.max(0, Math.min(150, (event.pageX || event.changedTouches[0].pageX) - left))) / 150);
let brightness = Math.floor(100 * (150 - Math.max(0, Math.min(150, ((event.pageY || event.changedTouches[0].pageY) - top)))) / 150); let brightness = Math.floor((100 * (150 - Math.max(0, Math.min(150, (event.pageY || event.changedTouches[0].pageY) - top)))) / 150);
this.hsbValue = this.validateHSB({ this.hsbValue = this.validateHSB({
h: this.hsbValue.h, h: this.hsbValue.h,
s: saturation, s: saturation,
@ -142,8 +138,9 @@ export default {
}, },
pickHue(event) { pickHue(event) {
let top = this.hueView.getBoundingClientRect().top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0); let top = this.hueView.getBoundingClientRect().top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
this.hsbValue = this.validateHSB({ this.hsbValue = this.validateHSB({
h: Math.floor(360 * (150 - Math.max(0, Math.min(150, ((event.pageY || event.changedTouches[0].pageY) - top)))) / 150), h: Math.floor((360 * (150 - Math.max(0, Math.min(150, (event.pageY || event.changedTouches[0].pageY) - top)))) / 150),
s: 100, s: 100,
b: 100 b: 100
}); });
@ -181,18 +178,19 @@ export default {
s: 100, s: 100,
b: 100 b: 100
}); });
this.colorSelector.style.backgroundColor = '#' + this.HSBtoHEX(hsbValue); this.colorSelector.style.backgroundColor = '#' + this.HSBtoHEX(hsbValue);
} }
}, },
updateColorHandle() { updateColorHandle() {
if (this.colorHandle) { if (this.colorHandle) {
this.colorHandle.style.left = Math.floor(150 * this.hsbValue.s / 100) + 'px'; this.colorHandle.style.left = Math.floor((150 * this.hsbValue.s) / 100) + 'px';
this.colorHandle.style.top = Math.floor(150 * (100 - this.hsbValue.b) / 100) + 'px'; this.colorHandle.style.top = Math.floor((150 * (100 - this.hsbValue.b)) / 100) + 'px';
} }
}, },
updateHue() { updateHue() {
if (this.hueHandle) { if (this.hueHandle) {
this.hueHandle.style.top = Math.floor(150 - (150 * this.hsbValue.h / 360)) + 'px'; this.hueHandle.style.top = Math.floor(150 - (150 * this.hsbValue.h) / 360) + 'px';
} }
}, },
updateInput() { updateInput() {
@ -222,19 +220,24 @@ export default {
}, },
validateHEX(hex) { validateHEX(hex) {
var len = 6 - hex.length; var len = 6 - hex.length;
if (len > 0) { if (len > 0) {
var o = []; var o = [];
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
o.push('0'); o.push('0');
} }
o.push(hex); o.push(hex);
hex = o.join(''); hex = o.join('');
} }
return hex; return hex;
}, },
HEXtoRGB(hex) { HEXtoRGB(hex) {
let hexValue = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); let hexValue = parseInt(hex.indexOf('#') > -1 ? hex.substring(1) : hex, 16);
return {r: hexValue >> 16, g: (hexValue & 0x00FF00) >> 8, b: (hexValue & 0x0000FF)};
return { r: hexValue >> 16, g: (hexValue & 0x00ff00) >> 8, b: hexValue & 0x0000ff };
}, },
HEXtoHSB(hex) { HEXtoHSB(hex) {
return this.RGBtoHSB(this.HEXtoRGB(hex)); return this.RGBtoHSB(this.HEXtoRGB(hex));
@ -248,8 +251,10 @@ export default {
var min = Math.min(rgb.r, rgb.g, rgb.b); var min = Math.min(rgb.r, rgb.g, rgb.b);
var max = Math.max(rgb.r, rgb.g, rgb.b); var max = Math.max(rgb.r, rgb.g, rgb.b);
var delta = max - min; var delta = max - min;
hsb.b = max; hsb.b = max;
hsb.s = max !== 0 ? 255 * delta / max : 0; hsb.s = max !== 0 ? (255 * delta) / max : 0;
if (hsb.s !== 0) { if (hsb.s !== 0) {
if (rgb.r === max) { if (rgb.r === max) {
hsb.h = (rgb.g - rgb.b) / delta; hsb.h = (rgb.g - rgb.b) / delta;
@ -261,49 +266,76 @@ export default {
} else { } else {
hsb.h = -1; hsb.h = -1;
} }
hsb.h *= 60; hsb.h *= 60;
if (hsb.h < 0) { if (hsb.h < 0) {
hsb.h += 360; hsb.h += 360;
} }
hsb.s *= 100 / 255; hsb.s *= 100 / 255;
hsb.b *= 100 / 255; hsb.b *= 100 / 255;
return hsb; return hsb;
}, },
HSBtoRGB(hsb) { HSBtoRGB(hsb) {
var rgb = { var rgb = {
r: null, g: null, b: null r: null,
g: null,
b: null
}; };
var h = Math.round(hsb.h); var h = Math.round(hsb.h);
var s = Math.round(hsb.s*255/100); var s = Math.round((hsb.s * 255) / 100);
var v = Math.round(hsb.b*255/100); var v = Math.round((hsb.b * 255) / 100);
if (s === 0) { if (s === 0) {
rgb = { rgb = {
r: v, r: v,
g: v, g: v,
b: v b: v
} };
} } else {
else {
var t1 = v; var t1 = v;
var t2 = (255-s)*v/255; var t2 = ((255 - s) * v) / 255;
var t3 = (t1-t2)*(h%60)/60; var t3 = ((t1 - t2) * (h % 60)) / 60;
if (h === 360) h = 0; if (h === 360) h = 0;
if (h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3}
else if (h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} if (h < 60) {
else if (h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} rgb.r = t1;
else if (h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} rgb.b = t2;
else if (h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} rgb.g = t2 + t3;
else if (h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} } else if (h < 120) {
else {rgb.r=0; rgb.g=0; rgb.b=0} rgb.g = t1;
rgb.b = t2;
rgb.r = t1 - t3;
} else if (h < 180) {
rgb.g = t1;
rgb.r = t2;
rgb.b = t2 + t3;
} else if (h < 240) {
rgb.b = t1;
rgb.r = t2;
rgb.g = t1 - t3;
} else if (h < 300) {
rgb.b = t1;
rgb.g = t2;
rgb.r = t2 + t3;
} else if (h < 360) {
rgb.r = t1;
rgb.g = t2;
rgb.b = t1 - t3;
} else {
rgb.r = 0;
rgb.g = 0;
rgb.b = 0;
} }
}
return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) }; return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) };
}, },
RGBtoHEX(rgb) { RGBtoHEX(rgb) {
var hex = [ var hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
rgb.r.toString(16),
rgb.g.toString(16),
rgb.b.toString(16)
];
for (var key in hex) { for (var key in hex) {
if (hex[key].length === 1) { if (hex[key].length === 1) {
@ -336,8 +368,7 @@ export default {
default: default:
break; break;
} }
} } else {
else {
hsb = this.HEXtoHSB(this.defaultColor); hsb = this.HEXtoHSB(this.defaultColor);
} }
@ -369,10 +400,8 @@ export default {
} }
}, },
alignOverlay() { alignOverlay() {
if (this.appendTo === 'self') if (this.appendTo === 'self') DomHandler.relativePosition(this.picker, this.$refs.input);
DomHandler.relativePosition(this.picker, this.$refs.input); else DomHandler.absolutePosition(this.picker, this.$refs.input);
else
DomHandler.absolutePosition(this.picker, this.$refs.input);
}, },
onInputClick() { onInputClick() {
if (this.disabled) { if (this.disabled) {
@ -470,6 +499,7 @@ export default {
this.overlayVisible = false; this.overlayVisible = false;
} }
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
@ -502,6 +532,7 @@ export default {
this.overlayVisible = false; this.overlayVisible = false;
} }
}; };
window.addEventListener('resize', this.resizeListener); window.addEventListener('resize', this.resizeListener);
} }
}, },
@ -536,7 +567,7 @@ export default {
} }
}, },
pickerRef(el) { pickerRef(el) {
this.picker = el this.picker = el;
}, },
colorSelectorRef(el) { colorSelectorRef(el) {
this.colorSelector = el; this.colorSelector = el;
@ -572,17 +603,22 @@ export default {
return ['p-colorpicker-preview p-inputtext', { 'p-disabled': this.disabled }]; return ['p-colorpicker-preview p-inputtext', { 'p-disabled': this.disabled }];
}, },
pickerClass() { pickerClass() {
return ['p-colorpicker-panel', this.panelClass, { return [
'p-colorpicker-overlay-panel': !this.inline, 'p-disabled': this.disabled, 'p-colorpicker-panel',
this.panelClass,
{
'p-colorpicker-overlay-panel': !this.inline,
'p-disabled': this.disabled,
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}]; }
];
} }
}, },
components: { components: {
'Portal': Portal Portal: Portal
}
} }
};
</script> </script>
<style> <style>
@ -642,7 +678,7 @@ export default {
border-style: solid; border-style: solid;
margin: -5px 0 0 -5px; margin: -5px 0 0 -5px;
cursor: pointer; cursor: pointer;
opacity: .85; opacity: 0.85;
} }
.p-colorpicker-panel .p-colorpicker-hue { .p-colorpicker-panel .p-colorpicker-hue {
@ -651,7 +687,7 @@ export default {
top: 8px; top: 8px;
left: 167px; left: 167px;
position: absolute; position: absolute;
opacity: .85; opacity: 0.85;
} }
.p-colorpicker-panel .p-colorpicker-hue-handle { .p-colorpicker-panel .p-colorpicker-hue-handle {
@ -664,7 +700,7 @@ export default {
height: 10px; height: 10px;
border-width: 2px; border-width: 2px;
border-style: solid; border-style: solid;
opacity: .85; opacity: 0.85;
cursor: pointer; cursor: pointer;
} }
</style> </style>

View file

@ -464,14 +464,13 @@ export interface ColumnSlots {
}) => VNode[]; }) => VNode[];
} }
export declare type ColumnEmits = { export declare type ColumnEmits = {};
}
declare class Column extends ClassComponent<ColumnProps, ColumnSlots, ColumnEmits> {} declare class Column extends ClassComponent<ColumnProps, ColumnSlots, ColumnEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Column: GlobalComponentConstructor<Column> Column: GlobalComponentConstructor<Column>;
} }
} }
@ -481,8 +480,8 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [DataTable](https://www.primefaces.org/primevue/showcase/#/datatable) * - [DataTable](https://www.primefaces.org/primevue/datatable)
* - [TreeTable](https://www.primefaces.org/primevue/showcase/#/treetable) * - [TreeTable](https://www.primefaces.org/primevue/treetable)
* *
*/ */
export default Column; export default Column;

View file

@ -182,5 +182,5 @@ export default {
render() { render() {
return null; return null;
} }
} };
</script> </script>

View file

@ -9,17 +9,15 @@ export interface ColumnGroupProps {
type?: ColumnGroupType; type?: ColumnGroupType;
} }
export interface ColumnGroupSlots { export interface ColumnGroupSlots {}
}
export declare type ColumnGroupEmits = { export declare type ColumnGroupEmits = {};
}
declare class ColumnGroup extends ClassComponent<ColumnGroupProps, ColumnGroupSlots, ColumnGroupEmits> {} declare class ColumnGroup extends ClassComponent<ColumnGroupProps, ColumnGroupSlots, ColumnGroupEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
ColumnGroup: GlobalComponentConstructor<ColumnGroup> ColumnGroup: GlobalComponentConstructor<ColumnGroup>;
} }
} }
@ -29,7 +27,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [ColumnGroup](https://www.primefaces.org/primevue/showcase/#/datatable/colgroup) * - [ColumnGroup](https://www.primefaces.org/primevue/datatable/colgroup)
* *
*/ */
export default ColumnGroup; export default ColumnGroup;

View file

@ -10,5 +10,5 @@ export default {
render() { render() {
return null; return null;
} }
} };
</script> </script>

View file

@ -24,6 +24,8 @@ interface PrimeVueLocaleAriaOptions {
selectAll?: string; selectAll?: string;
unselectAll?: string; unselectAll?: string;
close?: string; close?: string;
previous?: string;
next?: string;
} }
interface PrimeVueLocaleOptions { interface PrimeVueLocaleOptions {
@ -101,7 +103,7 @@ declare module 'vue/types/vue' {
interface Vue { interface Vue {
$primevue: { $primevue: {
config: PrimeVueConfiguration; config: PrimeVueConfiguration;
} };
} }
} }
@ -109,6 +111,6 @@ declare module '@vue/runtime-core' {
interface ComponentCustomProperties { interface ComponentCustomProperties {
$primevue: { $primevue: {
config: PrimeVueConfiguration; config: PrimeVueConfiguration;
} };
} }
} }

View file

@ -31,11 +31,11 @@ const defaultOptions = {
choose: 'Choose', choose: 'Choose',
upload: 'Upload', upload: 'Upload',
cancel: 'Cancel', cancel: 'Cancel',
dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
monthNames: ["January","February","March","April","May","June","July","August","September","October","November","December"], monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
chooseYear: 'Choose Year', chooseYear: 'Choose Year',
chooseMonth: 'Choose Month', chooseMonth: 'Choose Month',
chooseDate: 'Choose Date', chooseDate: 'Choose Date',
@ -75,32 +75,15 @@ const defaultOptions = {
stars: '{star} stars', stars: '{star} stars',
selectAll: 'All items selected', selectAll: 'All items selected',
unselectAll: 'All items unselected', unselectAll: 'All items unselected',
close: 'Close' close: 'Close',
previous: 'Previous',
next: 'Next'
} }
}, },
filterMatchModeOptions: { filterMatchModeOptions: {
text: [ text: [FilterMatchMode.STARTS_WITH, FilterMatchMode.CONTAINS, FilterMatchMode.NOT_CONTAINS, FilterMatchMode.ENDS_WITH, FilterMatchMode.EQUALS, FilterMatchMode.NOT_EQUALS],
FilterMatchMode.STARTS_WITH, numeric: [FilterMatchMode.EQUALS, FilterMatchMode.NOT_EQUALS, FilterMatchMode.LESS_THAN, FilterMatchMode.LESS_THAN_OR_EQUAL_TO, FilterMatchMode.GREATER_THAN, FilterMatchMode.GREATER_THAN_OR_EQUAL_TO],
FilterMatchMode.CONTAINS, date: [FilterMatchMode.DATE_IS, FilterMatchMode.DATE_IS_NOT, FilterMatchMode.DATE_BEFORE, FilterMatchMode.DATE_AFTER]
FilterMatchMode.NOT_CONTAINS,
FilterMatchMode.ENDS_WITH,
FilterMatchMode.EQUALS,
FilterMatchMode.NOT_EQUALS
],
numeric: [
FilterMatchMode.EQUALS,
FilterMatchMode.NOT_EQUALS,
FilterMatchMode.LESS_THAN,
FilterMatchMode.LESS_THAN_OR_EQUAL_TO,
FilterMatchMode.GREATER_THAN,
FilterMatchMode.GREATER_THAN_OR_EQUAL_TO
],
date: [
FilterMatchMode.DATE_IS,
FilterMatchMode.DATE_IS_NOT,
FilterMatchMode.DATE_BEFORE,
FilterMatchMode.DATE_AFTER
]
}, },
zIndex: { zIndex: {
modal: 1100, modal: 1100,
@ -114,6 +97,7 @@ const PrimeVueSymbol = Symbol();
export function usePrimeVue() { export function usePrimeVue() {
const PrimeVue = inject(PrimeVueSymbol); const PrimeVue = inject(PrimeVueSymbol);
if (!PrimeVue) { if (!PrimeVue) {
throw new Error('PrimeVue is not installed!'); throw new Error('PrimeVue is not installed!');
} }
@ -127,6 +111,7 @@ export default {
const PrimeVue = { const PrimeVue = {
config: reactive(configOptions) config: reactive(configOptions)
}; };
app.config.globalProperties.$primevue = PrimeVue; app.config.globalProperties.$primevue = PrimeVue;
app.provide(PrimeVueSymbol, PrimeVue); app.provide(PrimeVueSymbol, PrimeVue);
} }

View file

@ -39,6 +39,10 @@ export interface ConfirmationOptions {
* Callback to execute when action is rejected. * Callback to execute when action is rejected.
*/ */
reject?: () => void; reject?: () => void;
/**
* Callback to execute when dialog is hidden.
*/
onHide?: () => void;
/** /**
* Label of the accept button. Defaults to PrimeVue Locale configuration. * Label of the accept button. Defaults to PrimeVue Locale configuration.
*/ */

View file

@ -11,6 +11,7 @@ export default {
ConfirmationEventBus.emit('close'); ConfirmationEventBus.emit('close');
} }
}; };
app.config.globalProperties.$confirm = ConfirmationService; app.config.globalProperties.$confirm = ConfirmationService;
app.provide(PrimeVueConfirmSymbol, ConfirmationService); app.provide(PrimeVueConfirmSymbol, ConfirmationService);
} }

View file

@ -38,19 +38,16 @@ export interface ConfirmDialogSlots {
* Custom message template. * Custom message template.
* @param {Object} scope - message slot's params. * @param {Object} scope - message slot's params.
*/ */
message: (scope: { message: (scope: { message: ConfirmationOptions }) => VNode[];
message: ConfirmationOptions;
}) => VNode[];
} }
export declare type ConfirmDialogEmits = { export declare type ConfirmDialogEmits = {};
}
declare class ConfirmDialog extends ClassComponent<ConfirmDialogProps, ConfirmDialogSlots, ConfirmDialogEmits> {} declare class ConfirmDialog extends ClassComponent<ConfirmDialogProps, ConfirmDialogSlots, ConfirmDialogEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
ConfirmDialog: GlobalComponentConstructor<ConfirmDialog> ConfirmDialog: GlobalComponentConstructor<ConfirmDialog>;
} }
} }
@ -65,7 +62,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [ConfirmDialog](https://www.primefaces.org/primevue/showcase/#/confirmdialog) * - [ConfirmDialog](https://www.primefaces.org/primevue/confirmdialog)
* *
*/ */
export default ConfirmDialog; export default ConfirmDialog;

View file

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
import PrimeVue from '@/components/config/PrimeVue'; import PrimeVue from '@/components/config/PrimeVue';
import { mount } from '@vue/test-utils';
import ConfirmDialog from './ConfirmDialog.vue'; import ConfirmDialog from './ConfirmDialog.vue';
describe('ConfirmDialog', () => { describe('ConfirmDialog', () => {
@ -10,7 +10,7 @@ describe('ConfirmDialog', () => {
stubs: { stubs: {
teleport: true, teleport: true,
transition: false transition: false
}, }
}, },
data() { data() {
return { return {
@ -19,7 +19,7 @@ describe('ConfirmDialog', () => {
header: 'Confirmation', header: 'Confirmation',
icon: 'pi pi-exclamation-triangle' icon: 'pi pi-exclamation-triangle'
} }
} };
} }
}); });
@ -31,7 +31,7 @@ describe('ConfirmDialog', () => {
await wrapper.vm.reject(); await wrapper.vm.reject();
expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(false); expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(true);
}); });
it('should dialog trigger the accept function', async () => { it('should dialog trigger the accept function', async () => {
@ -50,14 +50,15 @@ describe('ConfirmDialog', () => {
header: 'Confirmation', header: 'Confirmation',
icon: 'pi pi-exclamation-triangle', icon: 'pi pi-exclamation-triangle',
accept: () => { accept: () => {
console.log('accept') // eslint-disable-next-line no-console
console.log('accept');
}, },
reject: () => { reject: () => {
// eslint-disable-next-line no-console
console.log('reject'); console.log('reject');
}
} }
} }
};
} }
}); });
@ -88,14 +89,15 @@ describe('ConfirmDialog', () => {
header: 'Confirmation', header: 'Confirmation',
icon: 'pi pi-exclamation-triangle', icon: 'pi pi-exclamation-triangle',
accept: () => { accept: () => {
console.log('accept') // eslint-disable-next-line no-console
console.log('accept');
}, },
reject: () => { reject: () => {
// eslint-disable-next-line no-console
console.log('reject'); console.log('reject');
}
} }
} }
};
} }
}); });
@ -126,7 +128,7 @@ describe('ConfirmDialog', () => {
header: 'Confirmation', header: 'Confirmation',
icon: 'pi pi-exclamation-triangle' icon: 'pi pi-exclamation-triangle'
} }
} };
} }
}); });
@ -136,7 +138,7 @@ describe('ConfirmDialog', () => {
await dialogCloseBtn.trigger('click'); await dialogCloseBtn.trigger('click');
expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(false); expect(wrapper.find('.p-dialog-mask .p-dialog.p-component').exists()).toBe(true);
}); });
it('should position work', async () => { it('should position work', async () => {
@ -157,7 +159,7 @@ describe('ConfirmDialog', () => {
icon: 'pi pi-info-circle', icon: 'pi pi-info-circle',
position: 'bottom' position: 'bottom'
} }
} };
} }
}); });

View file

@ -1,6 +1,5 @@
<template> <template>
<CDialog v-model:visible="visible" :modal="true" :header="header" :blockScroll="blockScroll" :position="position" class="p-confirm-dialog" <CDialog v-model:visible="visible" :modal="true" :header="header" :blockScroll="blockScroll" :position="position" class="p-confirm-dialog" :breakpoints="breakpoints" :closeOnEscape="closeOnEscape" @update:visible="onHide">
:breakpoints="breakpoints" :closeOnEscape="closeOnEscape">
<template v-if="!$slots.message"> <template v-if="!$slots.message">
<i :class="iconClass" /> <i :class="iconClass" />
<span class="p-confirm-dialog-message">{{ message }}</span> <span class="p-confirm-dialog-message">{{ message }}</span>
@ -14,9 +13,9 @@
</template> </template>
<script> <script>
import Button from 'primevue/button';
import ConfirmationEventBus from 'primevue/confirmationeventbus'; import ConfirmationEventBus from 'primevue/confirmationeventbus';
import Dialog from 'primevue/dialog'; import Dialog from 'primevue/dialog';
import Button from 'primevue/button';
export default { export default {
name: 'ConfirmDialog', name: 'ConfirmDialog',
@ -32,8 +31,8 @@ export default {
data() { data() {
return { return {
visible: false, visible: false,
confirmation: null, confirmation: null
} };
}, },
mounted() { mounted() {
this.confirmListener = (options) => { this.confirmListener = (options) => {
@ -51,6 +50,7 @@ export default {
this.visible = false; this.visible = false;
this.confirmation = null; this.confirmation = null;
}; };
ConfirmationEventBus.on('confirm', this.confirmListener); ConfirmationEventBus.on('confirm', this.confirmListener);
ConfirmationEventBus.on('close', this.closeListener); ConfirmationEventBus.on('close', this.closeListener);
}, },
@ -71,6 +71,13 @@ export default {
this.confirmation.reject(); this.confirmation.reject();
} }
this.visible = false;
},
onHide() {
if (this.confirmation.onHide) {
this.confirmation.onHide();
}
this.visible = false; this.visible = false;
} }
}, },
@ -91,10 +98,10 @@ export default {
return ['p-confirm-dialog-icon', this.confirmation ? this.confirmation.icon : null]; return ['p-confirm-dialog-icon', this.confirmation ? this.confirmation.icon : null];
}, },
acceptLabel() { acceptLabel() {
return this.confirmation ? (this.confirmation.acceptLabel || this.$primevue.config.locale.accept) : null; return this.confirmation ? this.confirmation.acceptLabel || this.$primevue.config.locale.accept : null;
}, },
rejectLabel() { rejectLabel() {
return this.confirmation ? (this.confirmation.rejectLabel || this.$primevue.config.locale.reject) : null; return this.confirmation ? this.confirmation.rejectLabel || this.$primevue.config.locale.reject : null;
}, },
acceptIcon() { acceptIcon() {
return this.confirmation ? this.confirmation.acceptIcon : null; return this.confirmation ? this.confirmation.acceptIcon : null;
@ -106,10 +113,10 @@ export default {
return ['p-confirm-dialog-accept', this.confirmation ? this.confirmation.acceptClass : null]; return ['p-confirm-dialog-accept', this.confirmation ? this.confirmation.acceptClass : null];
}, },
rejectClass() { rejectClass() {
return ['p-confirm-dialog-reject', this.confirmation ? (this.confirmation.rejectClass || 'p-button-text') : null]; return ['p-confirm-dialog-reject', this.confirmation ? this.confirmation.rejectClass || 'p-button-text' : null];
}, },
autoFocusAccept() { autoFocusAccept() {
return (this.confirmation.defaultFocus === undefined || this.confirmation.defaultFocus === 'accept') ? true : false; return this.confirmation.defaultFocus === undefined || this.confirmation.defaultFocus === 'accept' ? true : false;
}, },
autoFocusReject() { autoFocusReject() {
return this.confirmation.defaultFocus === 'reject' ? true : false; return this.confirmation.defaultFocus === 'reject' ? true : false;
@ -119,8 +126,8 @@ export default {
} }
}, },
components: { components: {
'CDialog': Dialog, CDialog: Dialog,
'CDButton': Button CDButton: Button
}
} }
};
</script> </script>

View file

@ -14,19 +14,16 @@ export interface ConfirmPopupSlots {
* Custom message template. * Custom message template.
* @param {Object} scope - message slot's params. * @param {Object} scope - message slot's params.
*/ */
message: (scope: { message: (scope: { message: ConfirmationOptions }) => VNode[];
message: ConfirmationOptions;
}) => VNode[];
} }
export declare type ConfirmPopupEmits = { export declare type ConfirmPopupEmits = {};
}
declare class ConfirmPopup extends ClassComponent<ConfirmPopupProps, ConfirmPopupSlots, ConfirmPopupEmits> {} declare class ConfirmPopup extends ClassComponent<ConfirmPopupProps, ConfirmPopupSlots, ConfirmPopupEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
ConfirmPopup: GlobalComponentConstructor<ConfirmPopup> ConfirmPopup: GlobalComponentConstructor<ConfirmPopup>;
} }
} }
@ -41,7 +38,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [ConfirmPopup](https://www.primefaces.org/primevue/showcase/#/confirmpopup) * - [ConfirmPopup](https://www.primefaces.org/primevue/confirmpopup)
* *
*/ */
export default ConfirmPopup; export default ConfirmPopup;

View file

@ -1,7 +1,7 @@
<template> <template>
<Portal> <Portal>
<transition name="p-confirm-popup" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave"> <transition name="p-confirm-popup" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave">
<div :class="containerClass" v-if="visible" :ref="containerRef" v-bind="$attrs" @click="onOverlayClick"> <div v-if="visible" :ref="containerRef" :class="containerClass" v-bind="$attrs" @click="onOverlayClick">
<template v-if="!$slots.message"> <template v-if="!$slots.message">
<div class="p-confirm-popup-content"> <div class="p-confirm-popup-content">
<i :class="iconClass" /> <i :class="iconClass" />
@ -35,7 +35,7 @@ export default {
return { return {
visible: false, visible: false,
confirmation: null confirmation: null
} };
}, },
target: null, target: null,
outsideClickListener: null, outsideClickListener: null,
@ -56,10 +56,12 @@ export default {
this.visible = true; this.visible = true;
} }
}; };
this.closeListener = () => { this.closeListener = () => {
this.visible = false; this.visible = false;
this.confirmation = null; this.confirmation = null;
}; };
ConfirmationEventBus.on('confirm', this.confirmListener); ConfirmationEventBus.on('confirm', this.confirmListener);
ConfirmationEventBus.on('close', this.closeListener); ConfirmationEventBus.on('close', this.closeListener);
}, },
@ -68,10 +70,12 @@ export default {
ConfirmationEventBus.off('close', this.closeListener); ConfirmationEventBus.off('close', this.closeListener);
this.unbindOutsideClickListener(); this.unbindOutsideClickListener();
if (this.scrollHandler) { if (this.scrollHandler) {
this.scrollHandler.destroy(); this.scrollHandler.destroy();
this.scrollHandler = null; this.scrollHandler = null;
} }
this.unbindResizeListener(); this.unbindResizeListener();
if (this.container) { if (this.container) {
@ -122,6 +126,7 @@ export default {
if (containerOffset.left < targetOffset.left) { if (containerOffset.left < targetOffset.left) {
arrowLeft = targetOffset.left - containerOffset.left; arrowLeft = targetOffset.left - containerOffset.left;
} }
this.container.style.setProperty('--overlayArrowLeft', `${arrowLeft}px`); this.container.style.setProperty('--overlayArrowLeft', `${arrowLeft}px`);
if (containerOffset.top < targetOffset.top) { if (containerOffset.top < targetOffset.top) {
@ -137,6 +142,7 @@ export default {
this.alignOverlay(); this.alignOverlay();
} }
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
@ -169,6 +175,7 @@ export default {
this.visible = false; this.visible = false;
} }
}; };
window.addEventListener('resize', this.resizeListener); window.addEventListener('resize', this.resizeListener);
} }
}, },
@ -193,10 +200,13 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-confirm-popup p-component', { return [
'p-confirm-popup p-component',
{
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}]; }
];
}, },
message() { message() {
return this.confirmation ? this.confirmation.message : null; return this.confirmation ? this.confirmation.message : null;
@ -205,10 +215,10 @@ export default {
return ['p-confirm-popup-icon', this.confirmation ? this.confirmation.icon : null]; return ['p-confirm-popup-icon', this.confirmation ? this.confirmation.icon : null];
}, },
acceptLabel() { acceptLabel() {
return this.confirmation ? (this.confirmation.acceptLabel || this.$primevue.config.locale.accept) : null; return this.confirmation ? this.confirmation.acceptLabel || this.$primevue.config.locale.accept : null;
}, },
rejectLabel() { rejectLabel() {
return this.confirmation ? (this.confirmation.rejectLabel || this.$primevue.config.locale.reject) : null; return this.confirmation ? this.confirmation.rejectLabel || this.$primevue.config.locale.reject : null;
}, },
acceptIcon() { acceptIcon() {
return this.confirmation ? this.confirmation.acceptIcon : null; return this.confirmation ? this.confirmation.acceptIcon : null;
@ -220,14 +230,14 @@ export default {
return ['p-confirm-popup-accept p-button-sm', this.confirmation ? this.confirmation.acceptClass : null]; return ['p-confirm-popup-accept p-button-sm', this.confirmation ? this.confirmation.acceptClass : null];
}, },
rejectClass() { rejectClass() {
return ['p-confirm-popup-reject p-button-sm', this.confirmation ? (this.confirmation.rejectClass || 'p-button-text') : null]; return ['p-confirm-popup-reject p-button-sm', this.confirmation ? this.confirmation.rejectClass || 'p-button-text' : null];
} }
}, },
components: { components: {
'CPButton': Button, CPButton: Button,
'Portal': Portal Portal: Portal
}
} }
};
</script> </script>
<style> <style>
@ -254,17 +264,18 @@ export default {
} }
.p-confirm-popup-enter-active { .p-confirm-popup-enter-active {
transition: transform .12s cubic-bezier(0, 0, 0.2, 1), opacity .12s cubic-bezier(0, 0, 0.2, 1); transition: transform 0.12s cubic-bezier(0, 0, 0.2, 1), opacity 0.12s cubic-bezier(0, 0, 0.2, 1);
} }
.p-confirm-popup-leave-active { .p-confirm-popup-leave-active {
transition: opacity .1s linear; transition: opacity 0.1s linear;
} }
.p-confirm-popup:after, .p-confirm-popup:before { .p-confirm-popup:after,
.p-confirm-popup:before {
bottom: 100%; bottom: 100%;
left: calc(var(--overlayArrowLeft, 0) + 1.25rem); left: calc(var(--overlayArrowLeft, 0) + 1.25rem);
content: " "; content: ' ';
height: 0; height: 0;
width: 0; width: 0;
position: absolute; position: absolute;
@ -281,7 +292,8 @@ export default {
margin-left: -10px; margin-left: -10px;
} }
.p-confirm-popup-flipped:after, .p-confirm-popup-flipped:before { .p-confirm-popup-flipped:after,
.p-confirm-popup-flipped:before {
bottom: auto; bottom: auto;
top: 100%; top: 100%;
} }
@ -291,7 +303,7 @@ export default {
} }
.p-confirm-popup.p-confirm-popup-flipped:before { .p-confirm-popup.p-confirm-popup-flipped:before {
border-bottom-color: transparent border-bottom-color: transparent;
} }
.p-confirm-popup .p-confirm-popup-content { .p-confirm-popup .p-confirm-popup-content {

View file

@ -49,8 +49,7 @@ export interface ContextMenuSlots {
}) => VNode[]; }) => VNode[];
} }
export declare type ContextMenuEmits = { export declare type ContextMenuEmits = {};
}
declare class ContextMenu extends ClassComponent<ContextMenuProps, ContextMenuSlots, ContextMenuEmits> { declare class ContextMenu extends ClassComponent<ContextMenuProps, ContextMenuSlots, ContextMenuEmits> {
/** /**
@ -77,7 +76,7 @@ declare class ContextMenu extends ClassComponent<ContextMenuProps, ContextMenuSl
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
ContextMenu: GlobalComponentConstructor<ContextMenu> ContextMenu: GlobalComponentConstructor<ContextMenu>;
} }
} }
@ -88,11 +87,11 @@ declare module '@vue/runtime-core' {
* *
* Helper API: * Helper API:
* *
* - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) * - [MenuItem](https://www.primefaces.org/primevue/menumodel)
* *
* Demos: * Demos:
* *
* - [ContextMenu](https://www.primefaces.org/primevue/showcase/#/contextmenu) * - [ContextMenu](https://www.primefaces.org/primevue/contextmenu)
* *
*/ */
export default ContextMenu; export default ContextMenu;

View file

@ -31,7 +31,7 @@ describe('ContextMenu.vue', () => {
{ {
label: 'Video', label: 'Video',
icon: 'pi pi-fw pi-video' icon: 'pi pi-fw pi-video'
}, }
] ]
}, },
{ {
@ -66,8 +66,7 @@ describe('ContextMenu.vue', () => {
{ {
label: 'Justify', label: 'Justify',
icon: 'pi pi-fw pi-align-justify' icon: 'pi pi-fw pi-align-justify'
}, }
] ]
}, },
{ {
@ -76,13 +75,11 @@ describe('ContextMenu.vue', () => {
items: [ items: [
{ {
label: 'New', label: 'New',
icon:'pi pi-fw pi-user-plus', icon: 'pi pi-fw pi-user-plus'
}, },
{ {
label: 'Delete', label: 'Delete',
icon:'pi pi-fw pi-user-minus', icon: 'pi pi-fw pi-user-minus'
}, },
{ {
label: 'Search', label: 'Search',
@ -121,7 +118,7 @@ describe('ContextMenu.vue', () => {
{ {
label: 'Delete', label: 'Delete',
icon: 'pi pi-fw pi-calendar-minus' icon: 'pi pi-fw pi-calendar-minus'
}, }
] ]
}, },
{ {

View file

@ -1,7 +1,7 @@
<template> <template>
<Portal :appendTo="appendTo"> <Portal :appendTo="appendTo">
<transition name="p-contextmenu" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave"> <transition name="p-contextmenu" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave">
<div :ref="containerRef" :class="containerClass" v-if="visible" v-bind="$attrs"> <div v-if="visible" :ref="containerRef" :class="containerClass" v-bind="$attrs">
<ContextMenuSub :model="model" :root="true" @leaf-click="onLeafClick" :template="$slots.item" :exact="exact" /> <ContextMenuSub :model="model" :root="true" @leaf-click="onLeafClick" :template="$slots.item" :exact="exact" />
</div> </div>
</transition> </transition>
@ -62,6 +62,7 @@ export default {
if (this.container && this.autoZIndex) { if (this.container && this.autoZIndex) {
ZIndexUtils.clear(this.container); ZIndexUtils.clear(this.container);
} }
this.container = null; this.container = null;
}, },
mounted() { mounted() {
@ -72,17 +73,17 @@ export default {
methods: { methods: {
itemClick(event) { itemClick(event) {
const item = event.item; const item = event.item;
if (item.command) { if (item.command) {
item.command(event); item.command(event);
event.originalEvent.preventDefault(); event.originalEvent.preventDefault();
} }
this.hide(); this.hide();
}, },
toggle(event) { toggle(event) {
if (this.visible) if (this.visible) this.hide();
this.hide(); else this.show(event);
else
this.show(event);
}, },
onLeafClick() { onLeafClick() {
this.hide(); this.hide();
@ -91,10 +92,8 @@ export default {
this.pageX = event.pageX; this.pageX = event.pageX;
this.pageY = event.pageY; this.pageY = event.pageY;
if (this.visible) if (this.visible) this.position();
this.position(); else this.visible = true;
else
this.visible = true;
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
@ -157,6 +156,7 @@ export default {
this.hide(); this.hide();
} }
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
@ -173,6 +173,7 @@ export default {
this.hide(); this.hide();
} }
}; };
window.addEventListener('resize', this.resizeListener); window.addEventListener('resize', this.resizeListener);
} }
}, },
@ -203,17 +204,20 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-contextmenu p-component', { return [
'p-contextmenu p-component',
{
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}] }
];
} }
}, },
components: { components: {
'ContextMenuSub': ContextMenuSub, ContextMenuSub: ContextMenuSub,
'Portal': Portal Portal: Portal
}
} }
};
</script> </script>
<style> <style>

View file

@ -1,28 +1,36 @@
<template> <template>
<transition name="p-contextmenusub" @enter="onEnter"> <transition name="p-contextmenusub" @enter="onEnter">
<ul ref="container" :class="containerClass" role="menu" v-if="root ? true : parentActive"> <ul v-if="root ? true : parentActive" ref="container" :class="containerClass" role="menu">
<template v-for="(item, i) of model" :key="label(item) + i.toString()"> <template v-for="(item, i) of model" :key="label(item) + i.toString()">
<li role="none" :class="getItemClass(item)" :style="item.style" v-if="visible(item) && !item.separator" <li v-if="visible(item) && !item.separator" role="none" :class="getItemClass(item)" :style="item.style" @mouseenter="onItemMouseEnter($event, item)">
@mouseenter="onItemMouseEnter($event, item)">
<template v-if="!template"> <template v-if="!template">
<router-link v-if="item.to && !disabled(item)" :to="item.to" custom v-slot="{navigate, href, isActive, isExactActive}"> <router-link v-if="item.to && !disabled(item)" v-slot="{ navigate, href, isActive, isExactActive }" :to="item.to" custom>
<a :href="href" @click="onItemClick($event, item, navigate)" :class="linkClass(item, {isActive, isExactActive})" v-ripple role="menuitem"> <a v-ripple :href="href" @click="onItemClick($event, item, navigate)" :class="linkClass(item, { isActive, isExactActive })" role="menuitem">
<span :class="['p-menuitem-icon', item.icon]" v-if="item.icon"></span> <span v-if="item.icon" :class="['p-menuitem-icon', item.icon]"></span>
<span class="p-menuitem-text">{{ label(item) }}</span> <span class="p-menuitem-text">{{ label(item) }}</span>
</a> </a>
</router-link> </router-link>
<a v-else :href="item.url" :class="linkClass(item)" :target="item.target" @click="onItemClick($event, item)" v-ripple <a
:aria-haspopup="item.items != null" :aria-expanded="item === activeItem" role="menuitem" :tabindex="disabled(item) ? null : '0'"> v-else
<span :class="['p-menuitem-icon', item.icon]" v-if="item.icon"></span> v-ripple
:href="item.url"
:class="linkClass(item)"
:target="item.target"
@click="onItemClick($event, item)"
:aria-haspopup="item.items != null"
:aria-expanded="item === activeItem"
role="menuitem"
:tabindex="disabled(item) ? null : '0'"
>
<span v-if="item.icon" :class="['p-menuitem-icon', item.icon]"></span>
<span class="p-menuitem-text">{{ label(item) }}</span> <span class="p-menuitem-text">{{ label(item) }}</span>
<span class="p-submenu-icon pi pi-angle-right" v-if="item.items"></span> <span v-if="item.items" class="p-submenu-icon pi pi-angle-right"></span>
</a> </a>
</template> </template>
<component v-else :is="template" :item="item"></component> <component v-else :is="template" :item="item"></component>
<ContextMenuSub :model="item.items" v-if="visible(item) && item.items" :key="label(item) + '_sub_'" :template="template" <ContextMenuSub v-if="visible(item) && item.items" :key="label(item) + '_sub_'" :model="item.items" :template="template" @leaf-click="onLeafClick" :parentActive="item === activeItem" :exact="exact" />
@leaf-click="onLeafClick" :parentActive="item === activeItem" :exact="exact" />
</li> </li>
<li :class="['p-menu-separator', item.class]" :style="item.style" v-if="visible(item) && item.separator" :key="'separator' + i.toString()" role="separator"></li> <li v-if="visible(item) && item.separator" :key="'separator' + i.toString()" :class="['p-menu-separator', item.class]" :style="item.style" role="separator"></li>
</template> </template>
</ul> </ul>
</transition> </transition>
@ -57,6 +65,11 @@ export default {
default: true default: true
} }
}, },
data() {
return {
activeItem: null
};
},
watch: { watch: {
parentActive(newValue) { parentActive(newValue) {
if (!newValue) { if (!newValue) {
@ -64,15 +77,11 @@ export default {
} }
} }
}, },
data() {
return {
activeItem: null
}
},
methods: { methods: {
onItemMouseEnter(event, item) { onItemMouseEnter(event, item) {
if (this.disabled(item)) { if (this.disabled(item)) {
event.preventDefault(); event.preventDefault();
return; return;
} }
@ -81,6 +90,7 @@ export default {
onItemClick(event, item, navigate) { onItemClick(event, item, navigate) {
if (this.disabled(item)) { if (this.disabled(item)) {
event.preventDefault(); event.preventDefault();
return; return;
} }
@ -92,10 +102,8 @@ export default {
} }
if (item.items) { if (item.items) {
if (this.activeItem && item === this.activeItem) if (this.activeItem && item === this.activeItem) this.activeItem = null;
this.activeItem = null; else this.activeItem = item;
else
this.activeItem = item;
} }
if (!item.items) { if (!item.items) {
@ -115,42 +123,46 @@ export default {
}, },
position() { position() {
const parentItem = this.$refs.container.parentElement; const parentItem = this.$refs.container.parentElement;
const containerOffset = DomHandler.getOffset(this.$refs.container.parentElement) const containerOffset = DomHandler.getOffset(this.$refs.container.parentElement);
const viewport = DomHandler.getViewport(); const viewport = DomHandler.getViewport();
const sublistWidth = this.$refs.container.offsetParent ? this.$refs.container.offsetWidth : DomHandler.getHiddenElementOuterWidth(this.$refs.container); const sublistWidth = this.$refs.container.offsetParent ? this.$refs.container.offsetWidth : DomHandler.getHiddenElementOuterWidth(this.$refs.container);
const itemOuterWidth = DomHandler.getOuterWidth(parentItem.children[0]); const itemOuterWidth = DomHandler.getOuterWidth(parentItem.children[0]);
this.$refs.container.style.top = '0px'; this.$refs.container.style.top = '0px';
if ((parseInt(containerOffset.left, 10) + itemOuterWidth + sublistWidth) > (viewport.width - DomHandler.calculateScrollbarWidth())) { if (parseInt(containerOffset.left, 10) + itemOuterWidth + sublistWidth > viewport.width - DomHandler.calculateScrollbarWidth()) {
this.$refs.container.style.left = -1 * sublistWidth + 'px'; this.$refs.container.style.left = -1 * sublistWidth + 'px';
} } else {
else {
this.$refs.container.style.left = itemOuterWidth + 'px'; this.$refs.container.style.left = itemOuterWidth + 'px';
} }
}, },
getItemClass(item) { getItemClass(item) {
return [ return [
'p-menuitem', item.class, { 'p-menuitem',
item.class,
{
'p-menuitem-active': this.activeItem === item 'p-menuitem-active': this.activeItem === item
} }
] ];
}, },
linkClass(item, routerProps) { linkClass(item, routerProps) {
return ['p-menuitem-link', { return [
'p-menuitem-link',
{
'p-disabled': this.disabled(item), 'p-disabled': this.disabled(item),
'router-link-active': routerProps && routerProps.isActive, 'router-link-active': routerProps && routerProps.isActive,
'router-link-active-exact': this.exact && routerProps && routerProps.isExactActive 'router-link-active-exact': this.exact && routerProps && routerProps.isExactActive
}]; }
];
}, },
visible(item) { visible(item) {
return (typeof item.visible === 'function' ? item.visible() : item.visible !== false); return typeof item.visible === 'function' ? item.visible() : item.visible !== false;
}, },
disabled(item) { disabled(item) {
return (typeof item.disabled === 'function' ? item.disabled() : item.disabled); return typeof item.disabled === 'function' ? item.disabled() : item.disabled;
}, },
label(item) { label(item) {
return (typeof item.label === 'function' ? item.label() : item.label); return typeof item.label === 'function' ? item.label() : item.label;
} }
}, },
computed: { computed: {
@ -159,7 +171,7 @@ export default {
} }
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}
} }
};
</script> </script>

View file

@ -4,29 +4,39 @@
</td> </td>
<td v-else :style="containerStyle" :class="containerClass" @click="onClick" @keydown="onKeyDown" role="cell"> <td v-else :style="containerStyle" :class="containerClass" @click="onClick" @keydown="onKeyDown" role="cell">
<span v-if="responsiveLayout === 'stack'" class="p-column-title">{{ columnProp('header') }}</span> <span v-if="responsiveLayout === 'stack'" class="p-column-title">{{ columnProp('header') }}</span>
<component :is="column.children.body" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" :editorInitCallback="editorInitCallback" v-if="column.children && column.children.body && !d_editing" /> <component v-if="column.children && column.children.body && !d_editing" :is="column.children.body" :data="rowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" :editorInitCallback="editorInitCallback" />
<component :is="column.children.editor" :data="editingRowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" :editorSaveCallback="editorSaveCallback" :editorCancelCallback="editorCancelCallback" v-else-if="column.children && column.children.editor && d_editing" /> <component
<component :is="column.children.body" :data="editingRowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" v-else-if="column.children && column.children.body && !column.children.editor && d_editing" /> v-else-if="column.children && column.children.editor && d_editing"
:is="column.children.editor"
:data="editingRowData"
:column="column"
:field="field"
:index="rowIndex"
:frozenRow="frozenRow"
:editorSaveCallback="editorSaveCallback"
:editorCancelCallback="editorCancelCallback"
/>
<component v-else-if="column.children && column.children.body && !column.children.editor && d_editing" :is="column.children.body" :data="editingRowData" :column="column" :field="field" :index="rowIndex" :frozenRow="frozenRow" />
<template v-else-if="columnProp('selectionMode')"> <template v-else-if="columnProp('selectionMode')">
<DTRadioButton :value="rowData" :checked="selected" @change="toggleRowWithRadio($event, rowIndex)" v-if="columnProp('selectionMode') === 'single'" /> <DTRadioButton v-if="columnProp('selectionMode') === 'single'" :value="rowData" :checked="selected" @change="toggleRowWithRadio($event, rowIndex)" />
<DTCheckbox :value="rowData" :checked="selected" @change="toggleRowWithCheckbox($event, rowIndex)" v-else-if="columnProp('selectionMode') ==='multiple'" /> <DTCheckbox v-else-if="columnProp('selectionMode') === 'multiple'" :value="rowData" :checked="selected" @change="toggleRowWithCheckbox($event, rowIndex)" />
</template> </template>
<template v-else-if="columnProp('rowReorder')"> <template v-else-if="columnProp('rowReorder')">
<i :class="['p-datatable-reorderablerow-handle', (columnProp('rowReorderIcon') || 'pi pi-bars')]"></i> <i :class="['p-datatable-reorderablerow-handle', columnProp('rowReorderIcon') || 'pi pi-bars']"></i>
</template> </template>
<template v-else-if="columnProp('expander')"> <template v-else-if="columnProp('expander')">
<button class="p-row-toggler p-link" @click="toggleRow" type="button" v-ripple> <button v-ripple class="p-row-toggler p-link" @click="toggleRow" type="button">
<span :class="rowTogglerIcon"></span> <span :class="rowTogglerIcon"></span>
</button> </button>
</template> </template>
<template v-else-if="editMode === 'row' && columnProp('rowEditor')"> <template v-else-if="editMode === 'row' && columnProp('rowEditor')">
<button class="p-row-editor-init p-link" v-if="!d_editing" @click="onRowEditInit" type="button" v-ripple> <button v-if="!d_editing" v-ripple class="p-row-editor-init p-link" @click="onRowEditInit" type="button">
<span class="p-row-editor-init-icon pi pi-fw pi-pencil"></span> <span class="p-row-editor-init-icon pi pi-fw pi-pencil"></span>
</button> </button>
<button class="p-row-editor-save p-link" v-if="d_editing" @click="onRowEditSave" type="button" v-ripple> <button v-if="d_editing" v-ripple class="p-row-editor-save p-link" @click="onRowEditSave" type="button">
<span class="p-row-editor-save-icon pi pi-fw pi-check"></span> <span class="p-row-editor-save-icon pi pi-fw pi-check"></span>
</button> </button>
<button class="p-row-editor-cancel p-link" v-if="d_editing" @click="onRowEditCancel" type="button" v-ripple> <button v-if="d_editing" v-ripple class="p-row-editor-cancel p-link" @click="onRowEditCancel" type="button">
<span class="p-row-editor-cancel-icon pi pi-fw pi-times"></span> <span class="p-row-editor-cancel-icon pi pi-fw pi-times"></span>
</button> </button>
</template> </template>
@ -43,8 +53,7 @@ import Ripple from 'primevue/ripple';
export default { export default {
name: 'BodyCell', name: 'BodyCell',
emits: ['cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', emits: ['cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'row-edit-init', 'row-edit-save', 'row-edit-cancel', 'row-toggle', 'radio-change', 'checkbox-change', 'editing-meta-change'],
'row-toggle', 'radio-change', 'checkbox-change', 'editing-meta-change'],
props: { props: {
rowData: { rowData: {
type: Object, type: Object,
@ -102,14 +111,14 @@ export default {
return { return {
d_editing: this.editing, d_editing: this.editing,
styleObject: {} styleObject: {}
} };
}, },
watch: { watch: {
editing(newValue) { editing(newValue) {
this.d_editing = newValue; this.d_editing = newValue;
}, },
'$data.d_editing': function (newValue) { '$data.d_editing': function (newValue) {
this.$emit('editing-meta-change', {data: this.rowData, field: (this.field || `field_${this.index}`), index: this.rowIndex, editing: newValue}); this.$emit('editing-meta-change', { data: this.rowData, field: this.field || `field_${this.index}`, index: this.rowIndex, editing: newValue });
} }
}, },
mounted() { mounted() {
@ -124,6 +133,7 @@ export default {
if (this.d_editing && (this.editMode === 'cell' || (this.editMode === 'row' && this.columnProp('rowEditor')))) { if (this.d_editing && (this.editMode === 'cell' || (this.editMode === 'row' && this.columnProp('rowEditor')))) {
const focusableEl = DomHandler.getFirstFocusableElement(this.$el); const focusableEl = DomHandler.getFirstFocusableElement(this.$el);
focusableEl && focusableEl.focus(); focusableEl && focusableEl.focus();
} }
}, },
@ -161,6 +171,7 @@ export default {
if (!this.selfClick) { if (!this.selfClick) {
this.completeEdit(event, 'outside'); this.completeEdit(event, 'outside');
} }
this.selfClick = false; this.selfClick = false;
}; };
@ -193,7 +204,8 @@ export default {
if (this.$el && this.$el.contains(e.target)) { if (this.$el && this.$el.contains(e.target)) {
this.selfClick = true; this.selfClick = true;
} }
} };
OverlayEventBus.on('overlay-click', this.overlayEventListener); OverlayEventBus.on('overlay-click', this.overlayEventListener);
} }
} }
@ -235,10 +247,8 @@ export default {
case 9: case 9:
this.completeEdit(event, 'tab'); this.completeEdit(event, 'tab');
if (event.shiftKey) if (event.shiftKey) this.moveToPreviousCell(event);
this.moveToPreviousCell(event); else this.moveToNextCell(event);
else
this.moveToNextCell(event);
break; break;
} }
} }
@ -264,13 +274,13 @@ export default {
findCell(element) { findCell(element) {
if (element) { if (element) {
let cell = element; let cell = element;
while (cell && !DomHandler.hasClass(cell, 'p-cell-editing')) { while (cell && !DomHandler.hasClass(cell, 'p-cell-editing')) {
cell = cell.parentElement; cell = cell.parentElement;
} }
return cell; return cell;
} } else {
else {
return null; return null;
} }
}, },
@ -279,18 +289,16 @@ export default {
if (!prevCell) { if (!prevCell) {
let previousRow = cell.parentElement.previousElementSibling; let previousRow = cell.parentElement.previousElementSibling;
if (previousRow) { if (previousRow) {
prevCell = previousRow.lastElementChild; prevCell = previousRow.lastElementChild;
} }
} }
if (prevCell) { if (prevCell) {
if (DomHandler.hasClass(prevCell, 'p-editable-column')) if (DomHandler.hasClass(prevCell, 'p-editable-column')) return prevCell;
return prevCell; else return this.findPreviousEditableColumn(prevCell);
else } else {
return this.findPreviousEditableColumn(prevCell);
}
else {
return null; return null;
} }
}, },
@ -299,23 +307,21 @@ export default {
if (!nextCell) { if (!nextCell) {
let nextRow = cell.parentElement.nextElementSibling; let nextRow = cell.parentElement.nextElementSibling;
if (nextRow) { if (nextRow) {
nextCell = nextRow.firstElementChild; nextCell = nextRow.firstElementChild;
} }
} }
if (nextCell) { if (nextCell) {
if (DomHandler.hasClass(nextCell, 'p-editable-column')) if (DomHandler.hasClass(nextCell, 'p-editable-column')) return nextCell;
return nextCell; else return this.findNextEditableColumn(nextCell);
else } else {
return this.findNextEditableColumn(nextCell);
}
else {
return null; return null;
} }
}, },
isEditingCellValid() { isEditingCellValid() {
return (DomHandler.find(this.$el, '.p-invalid').length === 0); return DomHandler.find(this.$el, '.p-invalid').length === 0;
}, },
onRowEditInit(event) { onRowEditInit(event) {
this.$emit('row-edit-init', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex }); this.$emit('row-edit-init', { originalEvent: event, data: this.rowData, newData: this.editingRowData, field: this.field, index: this.rowIndex });
@ -347,20 +353,24 @@ export default {
updateStickyPosition() { updateStickyPosition() {
if (this.columnProp('frozen')) { if (this.columnProp('frozen')) {
let align = this.columnProp('alignFrozen'); let align = this.columnProp('alignFrozen');
if (align === 'right') { if (align === 'right') {
let right = 0; let right = 0;
let next = this.$el.nextElementSibling; let next = this.$el.nextElementSibling;
if (next) { if (next) {
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0); right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0);
} }
this.styleObject.right = right + 'px'; this.styleObject.right = right + 'px';
} } else {
else {
let left = 0; let left = 0;
let prev = this.$el.previousElementSibling; let prev = this.$el.previousElementSibling;
if (prev) { if (prev) {
left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0); left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0);
} }
this.styleObject.left = left + 'px'; this.styleObject.left = left + 'px';
} }
} }
@ -377,12 +387,16 @@ export default {
return this.columnProp('field'); return this.columnProp('field');
}, },
containerClass() { containerClass() {
return [this.columnProp('bodyClass'), this.columnProp('class'), { return [
this.columnProp('bodyClass'),
this.columnProp('class'),
{
'p-selection-column': this.columnProp('selectionMode') != null, 'p-selection-column': this.columnProp('selectionMode') != null,
'p-editable-column': this.isEditable(), 'p-editable-column': this.isEditable(),
'p-cell-editing': this.d_editing, 'p-cell-editing': this.d_editing,
'p-frozen-column': this.columnProp('frozen') 'p-frozen-column': this.columnProp('frozen')
}]; }
];
}, },
containerStyle() { containerStyle() {
let bodyStyle = this.columnProp('bodyStyle'); let bodyStyle = this.columnProp('bodyStyle');
@ -395,23 +409,27 @@ export default {
}, },
loadingOptions() { loadingOptions() {
const getLoaderOptions = this.getVirtualScrollerProp('getLoaderOptions'); const getLoaderOptions = this.getVirtualScrollerProp('getLoaderOptions');
return getLoaderOptions && getLoaderOptions(this.rowIndex, {
return (
getLoaderOptions &&
getLoaderOptions(this.rowIndex, {
cellIndex: this.index, cellIndex: this.index,
cellFirst: this.index === 0, cellFirst: this.index === 0,
cellLast: this.index === (this.getVirtualScrollerProp('columns').length - 1), cellLast: this.index === this.getVirtualScrollerProp('columns').length - 1,
cellEven: this.index % 2 === 0, cellEven: this.index % 2 === 0,
cellOdd: this.index % 2 !== 0, cellOdd: this.index % 2 !== 0,
column: this.column, column: this.column,
field: this.field field: this.field
}); })
);
} }
}, },
components: { components: {
'DTRadioButton': RowRadioButton, DTRadioButton: RowRadioButton,
'DTCheckbox': RowCheckbox DTCheckbox: RowCheckbox
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}
} }
};
</script> </script>

View file

@ -1,40 +1,73 @@
<template> <template>
<div :class="containerClass"> <div :class="containerClass">
<div class="p-fluid p-column-filter-element" v-if="display === 'row'" > <div v-if="display === 'row'" class="p-fluid p-column-filter-element">
<component :is="filterElement" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" /> <component :is="filterElement" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" />
</div> </div>
<button ref="icon" v-if="showMenuButton" type="button" class="p-column-filter-menu-button p-link" aria-haspopup="true" :aria-expanded="overlayVisible" <button
v-if="showMenuButton"
ref="icon"
type="button"
class="p-column-filter-menu-button p-link"
aria-haspopup="true"
:aria-expanded="overlayVisible"
:class="{ 'p-column-filter-menu-button-open': overlayVisible, 'p-column-filter-menu-button-active': hasFilter() }" :class="{ 'p-column-filter-menu-button-open': overlayVisible, 'p-column-filter-menu-button-active': hasFilter() }"
@click="toggleMenu()" @keydown="onToggleButtonKeyDown($event)"><span class="pi pi-filter-icon pi-filter"></span></button> @click="toggleMenu()"
@keydown="onToggleButtonKeyDown($event)"
>
<span class="pi pi-filter-icon pi-filter"></span>
</button>
<button v-if="showClearButton && display === 'row'" :class="{ 'p-hidden-space': !hasRowFilter() }" type="button" class="p-column-filter-clear-button p-link" @click="clearFilter()"><span class="pi pi-filter-slash"></span></button> <button v-if="showClearButton && display === 'row'" :class="{ 'p-hidden-space': !hasRowFilter() }" type="button" class="p-column-filter-clear-button p-link" @click="clearFilter()"><span class="pi pi-filter-slash"></span></button>
<Portal> <Portal>
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave"> <transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave">
<div :ref="overlayRef" :class="overlayClass" v-if="overlayVisible" @keydown.escape="onEscape" @click="onContentClick" @mousedown="onContentMouseDown"> <div v-if="overlayVisible" :ref="overlayRef" :class="overlayClass" @keydown.escape="onEscape" @click="onContentClick" @mousedown="onContentMouseDown">
<component :is="filterHeaderTemplate" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" /> <component :is="filterHeaderTemplate" :field="field" :filterModel="filters[field]" :filterCallback="filterCallback" />
<template v-if="display === 'row'"> <template v-if="display === 'row'">
<ul class="p-column-filter-row-items"> <ul class="p-column-filter-row-items">
<li class="p-column-filter-row-item" v-for="(matchMode,i) of matchModes" :key="matchMode.label" <li
@click="onRowMatchModeChange(matchMode.value)" @keydown="onRowMatchModeKeyDown($event)" @keydown.enter.prevent="onRowMatchModeChange(matchMode.value)" v-for="(matchMode, i) of matchModes"
:class="{'p-highlight': isRowMatchModeSelected(matchMode.value)}" :tabindex="i === 0 ? '0' : null">{{matchMode.label}}</li> :key="matchMode.label"
class="p-column-filter-row-item"
@click="onRowMatchModeChange(matchMode.value)"
@keydown="onRowMatchModeKeyDown($event)"
@keydown.enter.prevent="onRowMatchModeChange(matchMode.value)"
:class="{ 'p-highlight': isRowMatchModeSelected(matchMode.value) }"
:tabindex="i === 0 ? '0' : null"
>
{{ matchMode.label }}
</li>
<li class="p-column-filter-separator"></li> <li class="p-column-filter-separator"></li>
<li class="p-column-filter-row-item" @click="clearFilter()" @keydown="onRowMatchModeKeyDown($event)" @keydown.enter="onRowClearItemClick()">{{ noFilterLabel }}</li> <li class="p-column-filter-row-item" @click="clearFilter()" @keydown="onRowMatchModeKeyDown($event)" @keydown.enter="onRowClearItemClick()">{{ noFilterLabel }}</li>
</ul> </ul>
</template> </template>
<template v-else> <template v-else>
<div class="p-column-filter-operator" v-if="isShowOperator"> <div v-if="isShowOperator" class="p-column-filter-operator">
<CFDropdown :options="operatorOptions" :modelValue="operator" @update:modelValue="onOperatorChange($event)" class="p-column-filter-operator-dropdown" optionLabel="label" optionValue="value"></CFDropdown> <CFDropdown :options="operatorOptions" :modelValue="operator" @update:modelValue="onOperatorChange($event)" class="p-column-filter-operator-dropdown" optionLabel="label" optionValue="value"></CFDropdown>
</div> </div>
<div class="p-column-filter-constraints"> <div class="p-column-filter-constraints">
<div v-for="(fieldConstraint, i) of fieldConstraints" :key="i" class="p-column-filter-constraint"> <div v-for="(fieldConstraint, i) of fieldConstraints" :key="i" class="p-column-filter-constraint">
<CFDropdown v-if="isShowMatchModes" :options="matchModes" :modelValue="fieldConstraint.matchMode" optionLabel="label" optionValue="value" <CFDropdown
@update:modelValue="onMenuMatchModeChange($event, i)" class="p-column-filter-matchmode-dropdown"></CFDropdown> v-if="isShowMatchModes"
:options="matchModes"
:modelValue="fieldConstraint.matchMode"
optionLabel="label"
optionValue="value"
@update:modelValue="onMenuMatchModeChange($event, i)"
class="p-column-filter-matchmode-dropdown"
></CFDropdown>
<component v-if="display === 'menu'" :is="filterElement" :field="field" :filterModel="fieldConstraint" :filterCallback="filterCallback" /> <component v-if="display === 'menu'" :is="filterElement" :field="field" :filterModel="fieldConstraint" :filterCallback="filterCallback" />
<div> <div>
<CFButton v-if="showRemoveIcon" type="button" icon="pi pi-trash" class="p-column-filter-remove-button p-button-text p-button-danger p-button-sm" @click="removeConstraint(i)" :label="removeRuleButtonLabel"></CFButton> <CFButton
v-if="showRemoveIcon"
type="button"
icon="pi pi-trash"
class="p-column-filter-remove-button p-button-text p-button-danger p-button-sm"
@click="removeConstraint(i)"
:label="removeRuleButtonLabel"
></CFButton>
</div> </div>
</div> </div>
</div> </div>
<div class="p-column-filter-add-rule" v-if="isShowAddConstraint"> <div v-if="isShowAddConstraint" class="p-column-filter-add-rule">
<CFButton type="button" :label="addRuleButtonLabel" icon="pi pi-plus" class="p-column-filter-add-button p-button-text p-button-sm" @click="addConstraint()"></CFButton> <CFButton type="button" :label="addRuleButtonLabel" icon="pi pi-plus" class="p-column-filter-add-button p-button-text p-button-sm" @click="addConstraint()"></CFButton>
</div> </div>
<div class="p-column-filter-buttonbar"> <div class="p-column-filter-buttonbar">
@ -140,7 +173,7 @@ export default {
overlayVisible: false, overlayVisible: false,
defaultMatchMode: null, defaultMatchMode: null,
defaultOperator: null defaultOperator: null
} };
}, },
overlay: null, overlay: null,
selfClick: false, selfClick: false,
@ -159,11 +192,11 @@ export default {
mounted() { mounted() {
if (this.filters && this.filters[this.field]) { if (this.filters && this.filters[this.field]) {
let fieldFilters = this.filters[this.field]; let fieldFilters = this.filters[this.field];
if (fieldFilters.operator) { if (fieldFilters.operator) {
this.defaultMatchMode = fieldFilters.constraints[0].matchMode; this.defaultMatchMode = fieldFilters.constraints[0].matchMode;
this.defaultOperator = fieldFilters.operator; this.defaultOperator = fieldFilters.operator;
} } else {
else {
this.defaultMatchMode = this.filters[this.field].matchMode; this.defaultMatchMode = this.filters[this.field].matchMode;
} }
} }
@ -171,12 +204,12 @@ export default {
methods: { methods: {
clearFilter() { clearFilter() {
let _filters = { ...this.filters }; let _filters = { ...this.filters };
if (_filters[this.field].operator) { if (_filters[this.field].operator) {
_filters[this.field].constraints.splice(1); _filters[this.field].constraints.splice(1);
_filters[this.field].operator = this.defaultOperator; _filters[this.field].operator = this.defaultOperator;
_filters[this.field].constraints[0] = { value: null, matchMode: this.defaultMatchMode }; _filters[this.field].constraints[0] = { value: null, matchMode: this.defaultMatchMode };
} } else {
else {
_filters[this.field].value = null; _filters[this.field].value = null;
_filters[this.field].matchMode = this.defaultMatchMode; _filters[this.field].matchMode = this.defaultMatchMode;
} }
@ -194,11 +227,10 @@ export default {
hasFilter() { hasFilter() {
if (this.filtersStore) { if (this.filtersStore) {
let fieldFilter = this.filtersStore[this.field]; let fieldFilter = this.filtersStore[this.field];
if (fieldFilter) { if (fieldFilter) {
if (fieldFilter.operator) if (fieldFilter.operator) return !this.isFilterBlank(fieldFilter.constraints[0].value);
return !this.isFilterBlank(fieldFilter.constraints[0].value); else return !this.isFilterBlank(fieldFilter.value);
else
return !this.isFilterBlank(fieldFilter.value);
} }
} }
@ -209,11 +241,10 @@ export default {
}, },
isFilterBlank(filter) { isFilterBlank(filter) {
if (filter !== null && filter !== undefined) { if (filter !== null && filter !== undefined) {
if ((typeof filter === 'string' && filter.trim().length == 0) || (filter instanceof Array && filter.length == 0)) if ((typeof filter === 'string' && filter.trim().length == 0) || (filter instanceof Array && filter.length == 0)) return true;
return true; else return false;
else
return false;
} }
return true; return true;
}, },
toggleMenu() { toggleMenu() {
@ -229,26 +260,30 @@ export default {
case 'ArrowDown': case 'ArrowDown':
if (this.overlayVisible) { if (this.overlayVisible) {
let focusable = DomHandler.getFocusableElements(this.overlay); let focusable = DomHandler.getFocusableElements(this.overlay);
if (focusable) { if (focusable) {
focusable[0].focus(); focusable[0].focus();
} }
event.preventDefault(); event.preventDefault();
} } else if (event.altKey) {
else if (event.altKey) {
this.overlayVisible = true; this.overlayVisible = true;
event.preventDefault(); event.preventDefault();
} }
break; break;
} }
}, },
onEscape() { onEscape() {
this.overlayVisible = false; this.overlayVisible = false;
if (this.$refs.icon) { if (this.$refs.icon) {
this.$refs.icon.focus(); this.$refs.icon.focus();
} }
}, },
onRowMatchModeChange(matchMode) { onRowMatchModeChange(matchMode) {
let _filters = { ...this.filters }; let _filters = { ...this.filters };
_filters[this.field].matchMode = matchMode; _filters[this.field].matchMode = matchMode;
this.$emit('matchmode-change', { field: this.field, matchMode: matchMode }); this.$emit('matchmode-change', { field: this.field, matchMode: matchMode });
this.$emit('filter-change', _filters); this.$emit('filter-change', _filters);
@ -261,6 +296,7 @@ export default {
switch (event.key) { switch (event.key) {
case 'ArrowDown': case 'ArrowDown':
var nextItem = this.findNextItem(item); var nextItem = this.findNextItem(item);
if (nextItem) { if (nextItem) {
item.removeAttribute('tabindex'); item.removeAttribute('tabindex');
nextItem.tabIndex = '0'; nextItem.tabIndex = '0';
@ -272,6 +308,7 @@ export default {
case 'ArrowUp': case 'ArrowUp':
var prevItem = this.findPrevItem(item); var prevItem = this.findPrevItem(item);
if (prevItem) { if (prevItem) {
item.removeAttribute('tabindex'); item.removeAttribute('tabindex');
prevItem.tabIndex = '0'; prevItem.tabIndex = '0';
@ -283,20 +320,23 @@ export default {
} }
}, },
isRowMatchModeSelected(matchMode) { isRowMatchModeSelected(matchMode) {
return (this.filters[this.field]).matchMode === matchMode; return this.filters[this.field].matchMode === matchMode;
}, },
onOperatorChange(value) { onOperatorChange(value) {
let _filters = { ...this.filters }; let _filters = { ...this.filters };
_filters[this.field].operator = value; _filters[this.field].operator = value;
this.$emit('filter-change', _filters); this.$emit('filter-change', _filters);
this.$emit('operator-change', { field: this.field, operator: value }); this.$emit('operator-change', { field: this.field, operator: value });
if (!this.showApplyButton) { if (!this.showApplyButton) {
this.$emit('filter-apply'); this.$emit('filter-apply');
} }
}, },
onMenuMatchModeChange(value, index) { onMenuMatchModeChange(value, index) {
let _filters = { ...this.filters }; let _filters = { ...this.filters };
_filters[this.field].constraints[index].matchMode = value; _filters[this.field].constraints[index].matchMode = value;
this.$emit('matchmode-change', { field: this.field, matchMode: value, index: index }); this.$emit('matchmode-change', { field: this.field, matchMode: value, index: index });
@ -307,6 +347,7 @@ export default {
addConstraint() { addConstraint() {
let _filters = { ...this.filters }; let _filters = { ...this.filters };
let newConstraint = { value: null, matchMode: this.defaultMatchMode }; let newConstraint = { value: null, matchMode: this.defaultMatchMode };
_filters[this.field].constraints.push(newConstraint); _filters[this.field].constraints.push(newConstraint);
this.$emit('constraint-add', { field: this.field, constraing: newConstraint }); this.$emit('constraint-add', { field: this.field, constraing: newConstraint });
this.$emit('filter-change', _filters); this.$emit('filter-change', _filters);
@ -318,6 +359,7 @@ export default {
removeConstraint(index) { removeConstraint(index) {
let _filters = { ...this.filters }; let _filters = { ...this.filters };
let removedConstraint = _filters[this.field].constraints.splice(index, 1); let removedConstraint = _filters[this.field].constraints.splice(index, 1);
this.$emit('constraint-remove', { field: this.field, constraing: removedConstraint }); this.$emit('constraint-remove', { field: this.field, constraing: removedConstraint });
this.$emit('filter-change', _filters); this.$emit('filter-change', _filters);
@ -331,18 +373,14 @@ export default {
findNextItem(item) { findNextItem(item) {
let nextItem = item.nextElementSibling; let nextItem = item.nextElementSibling;
if (nextItem) if (nextItem) return DomHandler.hasClass(nextItem, 'p-column-filter-separator') ? this.findNextItem(nextItem) : nextItem;
return DomHandler.hasClass(nextItem, 'p-column-filter-separator') ? this.findNextItem(nextItem) : nextItem; else return item.parentElement.firstElementChild;
else
return item.parentElement.firstElementChild;
}, },
findPrevItem(item) { findPrevItem(item) {
let prevItem = item.previousElementSibling; let prevItem = item.previousElementSibling;
if (prevItem) if (prevItem) DomHandler.hasClass(prevItem, 'p-column-filter-separator') ? this.findPrevItem(prevItem) : prevItem;
DomHandler.hasClass(prevItem, 'p-column-filter-separator') ? this.findPrevItem(prevItem) : prevItem; else return item.parentElement.lastElementChild;
else
return item.parentElement.lastElementChild;
}, },
hide() { hide() {
this.overlayVisible = false; this.overlayVisible = false;
@ -362,6 +400,7 @@ export default {
if (this.filterMenuStyle) { if (this.filterMenuStyle) {
DomHandler.applyStyle(this.overlay, this.filterMenuStyle); DomHandler.applyStyle(this.overlay, this.filterMenuStyle);
} }
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay); ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
DomHandler.absolutePosition(this.overlay, this.$refs.icon); DomHandler.absolutePosition(this.overlay, this.$refs.icon);
this.bindOutsideClickListener(); this.bindOutsideClickListener();
@ -372,7 +411,8 @@ export default {
if (!this.isOutsideClicked(e.target)) { if (!this.isOutsideClicked(e.target)) {
this.selfClick = true; this.selfClick = true;
} }
} };
OverlayEventBus.on('overlay-click', this.overlayEventListener); OverlayEventBus.on('overlay-click', this.overlayEventListener);
}, },
onOverlayLeave() { onOverlayLeave() {
@ -404,8 +444,10 @@ export default {
if (this.overlayVisible && !this.selfClick && this.isOutsideClicked(event.target)) { if (this.overlayVisible && !this.selfClick && this.isOutsideClicked(event.target)) {
this.overlayVisible = false; this.overlayVisible = false;
} }
this.selfClick = false; this.selfClick = false;
}; };
document.addEventListener('click', this.outsideClickListener); document.addEventListener('click', this.outsideClickListener);
} }
}, },
@ -439,6 +481,7 @@ export default {
this.hide(); this.hide();
} }
}; };
window.addEventListener('resize', this.resizeListener); window.addEventListener('resize', this.resizeListener);
} }
}, },
@ -451,27 +494,35 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-column-filter p-fluid', { return [
'p-column-filter p-fluid',
{
'p-column-filter-row': this.display === 'row', 'p-column-filter-row': this.display === 'row',
'p-column-filter-menu': this.display === 'menu' 'p-column-filter-menu': this.display === 'menu'
}]; }
];
}, },
overlayClass() { overlayClass() {
return [this.filterMenuClass, { return [
this.filterMenuClass,
{
'p-column-filter-overlay p-component p-fluid': true, 'p-column-filter-overlay p-component p-fluid': true,
'p-column-filter-overlay-menu': this.display === 'menu', 'p-column-filter-overlay-menu': this.display === 'menu',
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}]; }
];
}, },
showMenuButton() { showMenuButton() {
return this.showMenu && (this.display === 'row' ? this.type !== 'boolean' : true); return this.showMenu && (this.display === 'row' ? this.type !== 'boolean' : true);
}, },
matchModes() { matchModes() {
return this.matchModeOptions || return (
this.$primevue.config.filterMatchModeOptions[this.type].map(key => { this.matchModeOptions ||
return {label: this.$primevue.config.locale[key], value: key} this.$primevue.config.filterMatchModeOptions[this.type].map((key) => {
}); return { label: this.$primevue.config.locale[key], value: key };
})
);
}, },
isShowMatchModes() { isShowMatchModes() {
return this.type !== 'boolean' && this.showMatchModes && this.matchModes; return this.type !== 'boolean' && this.showMatchModes && this.matchModes;
@ -504,7 +555,7 @@ export default {
return this.$primevue.config.locale.addRule; return this.$primevue.config.locale.addRule;
}, },
isShowAddConstraint() { isShowAddConstraint() {
return this.showAddButton && this.filters[this.field].operator && (this.fieldConstraints && this.fieldConstraints.length < this.maxConstraints); return this.showAddButton && this.filters[this.field].operator && this.fieldConstraints && this.fieldConstraints.length < this.maxConstraints;
}, },
clearButtonLabel() { clearButtonLabel() {
return this.$primevue.config.locale.clear; return this.$primevue.config.locale.clear;
@ -514,9 +565,9 @@ export default {
} }
}, },
components: { components: {
'CFDropdown': Dropdown, CFDropdown: Dropdown,
'CFButton': Button, CFButton: Button,
'Portal': Portal Portal: Portal
}
} }
};
</script> </script>

View file

@ -907,17 +907,17 @@ export declare type DataTableEmits = {
* Callback to invoke on pagination. Sort and Filter information is also available for lazy loading implementation. * Callback to invoke on pagination. Sort and Filter information is also available for lazy loading implementation.
* @param {DataTablePageEvent} event - Custom page event. * @param {DataTablePageEvent} event - Custom page event.
*/ */
'page': (event: DataTablePageEvent) => void; page: (event: DataTablePageEvent) => void;
/** /**
* Callback to invoke on sort. Page and Filter information is also available for lazy loading implementation. * Callback to invoke on sort. Page and Filter information is also available for lazy loading implementation.
* @param {DataTableSortEvent} event - Custom sort event. * @param {DataTableSortEvent} event - Custom sort event.
*/ */
'sort': (event: DataTableSortEvent) => void; sort: (event: DataTableSortEvent) => void;
/** /**
* Event to emit after filtering, not triggered in lazy mode. * Event to emit after filtering, not triggered in lazy mode.
* @param {DataTableFilterEvent} event - Custom filter event. * @param {DataTableFilterEvent} event - Custom filter event.
*/ */
'filter': (event: DataTableFilterEvent) => void; filter: (event: DataTableFilterEvent) => void;
/** /**
* Callback to invoke after filtering, sorting, pagination and cell editing to pass the rendered value. * Callback to invoke after filtering, sorting, pagination and cell editing to pass the rendered value.
* @param {*} value - Value displayed by the table. * @param {*} value - Value displayed by the table.
@ -1038,7 +1038,7 @@ export declare type DataTableEmits = {
* @param {DataTableStateEvent} event - Custom state event. * @param {DataTableStateEvent} event - Custom state event.
*/ */
'state-save': (event: DataTableStateEvent) => void; 'state-save': (event: DataTableStateEvent) => void;
} };
declare class DataTable extends ClassComponent<DataTableProps, DataTableSlots, DataTableEmits> { declare class DataTable extends ClassComponent<DataTableProps, DataTableSlots, DataTableEmits> {
/** /**
@ -1051,7 +1051,7 @@ declare class DataTable extends ClassComponent<DataTableProps, DataTableSlots, D
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
DataTable: GlobalComponentConstructor<DataTable> DataTable: GlobalComponentConstructor<DataTable>;
} }
} }
@ -1066,10 +1066,10 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [DataTable](https://www.primefaces.org/primevue/showcase/#/datatable) * - [DataTable](https://www.primefaces.org/primevue/datatable)
* - [Edit](https://www.primefaces.org/primevue/showcase/#/datatable/edit) * - [Edit](https://www.primefaces.org/primevue/datatable/edit)
* - [Sort](https://www.primefaces.org/primevue/showcase/#/datatable/sort) * - [Sort](https://www.primefaces.org/primevue/datatable/sort)
* - [Filter](https://www.primefaces.org/primevue/showcase/#/datatable/filter) * - [Filter](https://www.primefaces.org/primevue/datatable/filter)
* etc. * etc.
* *
*/ */

View file

@ -11,72 +11,72 @@ window.URL.createObjectURL = function() {};
const smallData = [ const smallData = [
{ {
"id": "1000", id: '1000',
"code": "vbb124btr", code: 'vbb124btr',
"name": "Game Controller" name: 'Game Controller'
}, },
{ {
"id": "1001", id: '1001',
"code": "nvklal433", code: 'nvklal433',
"name": "Black Watch" name: 'Black Watch'
}, },
{ {
"id": "1002", id: '1002',
"code": "zz21cz3c1", code: 'zz21cz3c1',
"name": "Blue Band" name: 'Blue Band'
} }
]; ];
const data = [ const data = [
{ {
"id": "1000", id: '1000',
"code": "vbb124btr", code: 'vbb124btr',
"name": "Game Controller" name: 'Game Controller'
}, },
{ {
"id": "1001", id: '1001',
"code": "nvklal433", code: 'nvklal433',
"name": "Black Watch" name: 'Black Watch'
}, },
{ {
"id": "1002", id: '1002',
"code": "zz21cz3c1", code: 'zz21cz3c1',
"name": "Blue Band" name: 'Blue Band'
}, },
{ {
"id": "1003", id: '1003',
"code": "244wgerg2", code: '244wgerg2',
"name": "Blue T-Shirt" name: 'Blue T-Shirt'
}, },
{ {
"id": "1004", id: '1004',
"code": "h456wer53", code: 'h456wer53',
"name": "Bracelet" name: 'Bracelet'
}, },
{ {
"id": "1005", id: '1005',
"code": "cm230f032", code: 'cm230f032',
"name": "Gaming Set" name: 'Gaming Set'
}, },
{ {
"id": "1006", id: '1006',
"code": "bib36pfvm", code: 'bib36pfvm',
"name": "Chakra Bracelet" name: 'Chakra Bracelet'
}, },
{ {
"id": "1007", id: '1007',
"code": "mbvjkgip5", code: 'mbvjkgip5',
"name": "Galaxy Earrings" name: 'Galaxy Earrings'
}, },
{ {
"id": "1008", id: '1008',
"code": "f230fh0g3", code: 'f230fh0g3',
"name": "Bamboo Watch" name: 'Bamboo Watch'
}, },
{ {
"id": "1009", id: '1009',
"code": "av2231fwg", code: 'av2231fwg',
"name": "Brown Purse" name: 'Brown Purse'
} }
]; ];
@ -133,7 +133,6 @@ describe('DataTable.vue', () => {
expect(rows[0].findAll('td').length).toEqual(3); expect(rows[0].findAll('td').length).toEqual(3);
}); });
// table templating // table templating
it('should have header template', () => { it('should have header template', () => {
expect(wrapper.find('.p-datatable-header').exists()).toBe(true); expect(wrapper.find('.p-datatable-header').exists()).toBe(true);
@ -173,10 +172,8 @@ describe('DataTable.vue', () => {
expect(wrapper.find('.p-paginator-right-content').text()).toBe('Paginator End Templating'); expect(wrapper.find('.p-paginator-right-content').text()).toBe('Paginator End Templating');
}); });
// column templating // column templating
// column grouping // column grouping
it('should exist', () => { it('should exist', () => {
wrapper = mount({ wrapper = mount({
@ -216,7 +213,7 @@ describe('DataTable.vue', () => {
data() { data() {
return { return {
sales: null sales: null
} };
}, },
created() { created() {
this.sales = [ this.sales = [
@ -233,6 +230,7 @@ describe('DataTable.vue', () => {
computed: { computed: {
lastYearTotal() { lastYearTotal() {
let total = 0; let total = 0;
for (let sale of this.sales) { for (let sale of this.sales) {
total += sale.lastYearProfit; total += sale.lastYearProfit;
} }
@ -241,6 +239,7 @@ describe('DataTable.vue', () => {
}, },
thisYearTotal() { thisYearTotal() {
let total = 0; let total = 0;
for (let sale of this.sales) { for (let sale of this.sales) {
total += sale.thisYearProfit; total += sale.thisYearProfit;
} }
@ -281,7 +280,6 @@ describe('DataTable.vue', () => {
expect(footerRows[0].findAll('td')[2].text()).toEqual('This Year Total'); expect(footerRows[0].findAll('td')[2].text()).toEqual('This Year Total');
}); });
// sorting // sorting
it('should single sort', async () => { it('should single sort', async () => {
wrapper = mount(DataTable, { wrapper = mount(DataTable, {
@ -422,10 +420,9 @@ describe('DataTable.vue', () => {
expect(sortableTH.attributes()['aria-sort']).toBe('none'); expect(sortableTH.attributes()['aria-sort']).toBe('none');
}); });
// filtering // filtering
it('should filtered globally', async () => { it('should filtered globally', async () => {
await wrapper.setProps({ filters: { 'global': {value: 'b', matchMode: FilterMatchMode.STARTS_WITH} }}); await wrapper.setProps({ filters: { global: { value: 'b', matchMode: FilterMatchMode.STARTS_WITH } } });
await wrapper.vm.filter(smallData); await wrapper.vm.filter(smallData);
@ -433,10 +430,7 @@ describe('DataTable.vue', () => {
}); });
it('should filtered with menu display', async () => { it('should filtered with menu display', async () => {
await wrapper.setProps({ filters: { 'name': {value: 'b', matchMode: FilterMatchMode.STARTS_WITH} }, await wrapper.setProps({ filters: { name: { value: 'b', matchMode: FilterMatchMode.STARTS_WITH } }, filterDisplay: 'menu', globalFilterFields: ['name'] });
filterDisplay: 'menu',
globalFilterFields: ['name']
});
await wrapper.vm.filter(smallData); await wrapper.vm.filter(smallData);
@ -445,10 +439,7 @@ describe('DataTable.vue', () => {
}); });
it('should filtered with row display', async () => { it('should filtered with row display', async () => {
await wrapper.setProps({ filters: { 'name': {value: 'b', matchMode: FilterMatchMode.STARTS_WITH} }, await wrapper.setProps({ filters: { name: { value: 'b', matchMode: FilterMatchMode.STARTS_WITH } }, filterDisplay: 'row', globalFilterFields: ['name'] });
filterDisplay: 'row',
globalFilterFields: ['name']
});
await wrapper.vm.filter(smallData); await wrapper.vm.filter(smallData);
@ -635,7 +626,6 @@ describe('DataTable.vue', () => {
expect(wrapper.emitted()['update:selection'][0][0]).toEqual([]); expect(wrapper.emitted()['update:selection'][0][0]).toEqual([]);
}); });
// scrolling // scrolling
it('should scrolling', async () => { it('should scrolling', async () => {
await wrapper.setProps({ scrollable: true }); await wrapper.setProps({ scrollable: true });
@ -697,10 +687,8 @@ describe('DataTable.vue', () => {
// expect(wrapper.findAll('td.p-frozen-column')[0].attributes().style).toBe('left: 0px;'); // expect(wrapper.findAll('td.p-frozen-column')[0].attributes().style).toBe('left: 0px;');
}); });
// lazy loading // lazy loading
// row expansion // row expansion
it('should have row toggler', () => { it('should have row toggler', () => {
expect(wrapper.findAll('.p-row-toggler').length).toBe(3); expect(wrapper.findAll('.p-row-toggler').length).toBe(3);
@ -724,7 +712,6 @@ describe('DataTable.vue', () => {
expect(wrapper.emitted()['row-collapse'][0][0].data).toEqual(smallData[0]); expect(wrapper.emitted()['row-collapse'][0][0].data).toEqual(smallData[0]);
}); });
// editing // editing
// cell editing // cell editing
@ -799,10 +786,10 @@ describe('DataTable.vue', () => {
} }
}); });
await wrapper.vm.onRowEditSave({data: { "id": "9999", "code": "vbb124btr", "name": "Game Controller"}}); await wrapper.vm.onRowEditSave({ data: { id: '9999', code: 'vbb124btr', name: 'Game Controller' } });
expect(wrapper.emitted()['update:editingRows'][0][0]).toEqual([]); expect(wrapper.emitted()['update:editingRows'][0][0]).toEqual([]);
expect(wrapper.emitted()['row-edit-save'][0][0].data).toEqual({ "id": "9999", "code": "vbb124btr", "name": "Game Controller"}); expect(wrapper.emitted()['row-edit-save'][0][0].data).toEqual({ id: '9999', code: 'vbb124btr', name: 'Game Controller' });
}); });
it('should cancel row editing', async () => { it('should cancel row editing', async () => {
@ -841,7 +828,6 @@ describe('DataTable.vue', () => {
expect(wrapper.emitted()['row-edit-cancel'][0][0].data).toEqual(smallData[0]); expect(wrapper.emitted()['row-edit-cancel'][0][0].data).toEqual(smallData[0]);
}); });
// column resize // column resize
it('should fit mode expanding exists', () => { it('should fit mode expanding exists', () => {
wrapper = mount(DataTable, { wrapper = mount(DataTable, {
@ -1032,7 +1018,6 @@ describe('DataTable.vue', () => {
expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: none;'); expect(wrapper.find('.p-column-resizer-helper').attributes().style).toContain('display: none;');
}); });
// column reorder // column reorder
it('should reorder columns', async () => { it('should reorder columns', async () => {
await wrapper.setProps({ reorderableColumns: true }); await wrapper.setProps({ reorderableColumns: true });
@ -1041,7 +1026,6 @@ describe('DataTable.vue', () => {
expect(wrapper.find('.p-datatable-reorder-indicator-down').exists()).toBe(true); expect(wrapper.find('.p-datatable-reorder-indicator-down').exists()).toBe(true);
}); });
// row reorder // row reorder
it('should exist', () => { it('should exist', () => {
wrapper = mount(DataTable, { wrapper = mount(DataTable, {
@ -1065,7 +1049,6 @@ describe('DataTable.vue', () => {
expect(wrapper.findAll('.p-datatable-reorderablerow-handle').length).toBe(3); expect(wrapper.findAll('.p-datatable-reorderablerow-handle').length).toBe(3);
}); });
// row group // row group
// subheader grouping // subheader grouping
it('should exist', () => { it('should exist', () => {
@ -1169,81 +1152,81 @@ describe('DataTable.vue', () => {
props: { props: {
value: [ value: [
{ {
"id":1000, id: 1000,
"name":"James Butt", name: 'James Butt',
"country":{ country: {
"name":"Algeria", name: 'Algeria',
"code":"dz" code: 'dz'
}, },
"company":"Benton, John B Jr", company: 'Benton, John B Jr',
"representative":{ representative: {
"name":"Ioni Bowcher", name: 'Ioni Bowcher',
"image":"ionibowcher.png" image: 'ionibowcher.png'
} }
}, },
{ {
"id":1001, id: 1001,
"name":"Josephine Darakjy", name: 'Josephine Darakjy',
"country":{ country: {
"name":"Egypt", name: 'Egypt',
"code":"eg" code: 'eg'
}, },
"company":"Chanay, Jeffrey A Esq", company: 'Chanay, Jeffrey A Esq',
"representative":{ representative: {
"name":"Amy Elsner", name: 'Amy Elsner',
"image":"amyelsner.png" image: 'amyelsner.png'
} }
}, },
{ {
"id":1013, id: 1013,
"name":"Graciela Ruta", name: 'Graciela Ruta',
"country":{ country: {
"name":"Chile", name: 'Chile',
"code":"cl" code: 'cl'
}, },
"company":"Buckley Miller & Wright", company: 'Buckley Miller & Wright',
"representative":{ representative: {
"name":"Amy Elsner", name: 'Amy Elsner',
"image":"amyelsner.png" image: 'amyelsner.png'
} }
}, },
{ {
"id":1021, id: 1021,
"name":"Veronika Inouye", name: 'Veronika Inouye',
"country":{ country: {
"name":"Ecuador", name: 'Ecuador',
"code":"ec" code: 'ec'
}, },
"company":"C 4 Network Inc", company: 'C 4 Network Inc',
"representative":{ representative: {
"name":"Ioni Bowcher", name: 'Ioni Bowcher',
"image":"ionibowcher.png" image: 'ionibowcher.png'
} }
}, },
{ {
"id":1026, id: 1026,
"name":"Chanel Caudy", name: 'Chanel Caudy',
"country":{ country: {
"name":"Argentina", name: 'Argentina',
"code":"ar" code: 'ar'
}, },
"company":"Professional Image Inc", company: 'Professional Image Inc',
"representative":{ representative: {
"name":"Ioni Bowcher", name: 'Ioni Bowcher',
"image":"ionibowcher.png" image: 'ionibowcher.png'
} }
}, },
{ {
"id":1027, id: 1027,
"name":"Ezekiel Chui", name: 'Ezekiel Chui',
"country":{ country: {
"name":"Ireland", name: 'Ireland',
"code":"ie" code: 'ie'
}, },
"company":"Sider, Donald C Esq", company: 'Sider, Donald C Esq',
"representative":{ representative: {
"name":"Amy Elsner", name: 'Amy Elsner',
"image":"amyelsner.png" image: 'amyelsner.png'
} }
} }
], ],
@ -1294,34 +1277,34 @@ describe('DataTable.vue', () => {
props: { props: {
value: [ value: [
{ {
"id": "1000", id: '1000',
"code": "vbb124btr", code: 'vbb124btr',
"name": "Game Controller" name: 'Game Controller'
}, },
{ {
"id": "1001", id: '1001',
"code": "nvklal433", code: 'nvklal433',
"name": "Black Watch" name: 'Black Watch'
}, },
{ {
"id": "1002", id: '1002',
"code": "zz21cz3c1", code: 'zz21cz3c1',
"name": "Blue Band" name: 'Blue Band'
}, },
{ {
"id": "1003", id: '1003',
"code": "vbb124btrvbb124btr", code: 'vbb124btrvbb124btr',
"name": "Game Controller" name: 'Game Controller'
}, },
{ {
"id": "1004", id: '1004',
"code": "nvklal433nvklal433", code: 'nvklal433nvklal433',
"name": "Black Watch" name: 'Black Watch'
}, },
{ {
"id": "1006", id: '1006',
"code": "zz21cz3c1zz21cz3c1", code: 'zz21cz3c1zz21cz3c1',
"name": "Blue Band" name: 'Blue Band'
} }
], ],
rowGroupMode: 'rowspan', rowGroupMode: 'rowspan',
@ -1353,7 +1336,6 @@ describe('DataTable.vue', () => {
expect(wrapper.findAll('.p-datatable-tbody > tr')[4].findAll('td')[1].attributes().rowspan).toBe('2'); expect(wrapper.findAll('.p-datatable-tbody > tr')[4].findAll('td')[1].attributes().rowspan).toBe('2');
}); });
// export // export
it('should export table', async () => { it('should export table', async () => {
const exportCSV = jest.spyOn(wrapper.vm, 'exportCSV'); const exportCSV = jest.spyOn(wrapper.vm, 'exportCSV');
@ -1363,7 +1345,6 @@ describe('DataTable.vue', () => {
expect(exportCSV).toHaveBeenCalled(); expect(exportCSV).toHaveBeenCalled();
}); });
// state // state
it('should get storage', async () => { it('should get storage', async () => {
await wrapper.setProps({ stateStorage: 'session', stateKey: 'dt-state-demo-session', paginator: true }); await wrapper.setProps({ stateStorage: 'session', stateKey: 'dt-state-demo-session', paginator: true });
@ -1393,10 +1374,8 @@ describe('DataTable.vue', () => {
expect(wrapper.emitted()['state-save'][0]).not.toBeNull(); expect(wrapper.emitted()['state-save'][0]).not.toBeNull();
}); });
// contextmenu // contextmenu
// responsive // responsive
it('should have stack layout', () => { it('should have stack layout', () => {
expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-responsive-stack'); expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-responsive-stack');
@ -1408,6 +1387,5 @@ describe('DataTable.vue', () => {
expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-responsive-scroll'); expect(wrapper.find('.p-datatable').classes()).toContain('p-datatable-responsive-scroll');
}); });
// row styling // row styling
}); });

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
<template> <template>
<td :style="containerStyle" :class="containerClass" role="cell" <td :style="containerStyle" :class="containerClass" role="cell" :colspan="columnProp('colspan')" :rowspan="columnProp('rowspan')">
:colspan="columnProp('colspan')" :rowspan="columnProp('rowspan')"> <component v-if="column.children && column.children.footer" :is="column.children.footer" :column="column" />
<component :is="column.children.footer" :column="column" v-if="column.children && column.children.footer"/>
{{ columnProp('footer') }} {{ columnProp('footer') }}
</td> </td>
</template> </template>
@ -20,7 +19,7 @@ export default {
data() { data() {
return { return {
styleObject: {} styleObject: {}
} };
}, },
mounted() { mounted() {
if (this.columnProp('frozen')) { if (this.columnProp('frozen')) {
@ -39,20 +38,24 @@ export default {
updateStickyPosition() { updateStickyPosition() {
if (this.columnProp('frozen')) { if (this.columnProp('frozen')) {
let align = this.columnProp('alignFrozen'); let align = this.columnProp('alignFrozen');
if (align === 'right') { if (align === 'right') {
let right = 0; let right = 0;
let next = this.$el.nextElementSibling; let next = this.$el.nextElementSibling;
if (next) { if (next) {
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.left); right = DomHandler.getOuterWidth(next) + parseFloat(next.style.left);
} }
this.styleObject.right = right + 'px'; this.styleObject.right = right + 'px';
} } else {
else {
let left = 0; let left = 0;
let prev = this.$el.previousElementSibling; let prev = this.$el.previousElementSibling;
if (prev) { if (prev) {
left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left); left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left);
} }
this.styleObject.left = left + 'px'; this.styleObject.left = left + 'px';
} }
} }
@ -60,9 +63,13 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return [this.columnProp('footerClass'), this.columnProp('class'), { return [
this.columnProp('footerClass'),
this.columnProp('class'),
{
'p-frozen-column': this.columnProp('frozen') 'p-frozen-column': this.columnProp('frozen')
}]; }
];
}, },
containerStyle() { containerStyle() {
let bodyStyle = this.columnProp('footerStyle'); let bodyStyle = this.columnProp('footerStyle');
@ -71,5 +78,5 @@ export default {
return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject] : [columnStyle, bodyStyle]; return this.columnProp('frozen') ? [columnStyle, bodyStyle, this.styleObject] : [columnStyle, bodyStyle];
} }
} }
} };
</script> </script>

View file

@ -1,23 +1,57 @@
<template> <template>
<th :style="containerStyle" :class="containerClass" :tabindex="columnProp('sortable') ? '0' : null" role="cell" <th
@click="onClick" @keydown="onKeyDown" @mousedown="onMouseDown" :style="containerStyle"
@dragstart="onDragStart" @dragover="onDragOver" @dragleave="onDragLeave" @drop="onDrop" :class="containerClass"
:colspan="columnProp('colspan')" :rowspan="columnProp('rowspan')" :aria-sort="ariaSort"> :tabindex="columnProp('sortable') ? '0' : null"
<span class="p-column-resizer" @mousedown="onResizeStart" v-if="resizableColumns && !columnProp('frozen')"></span> role="cell"
@click="onClick"
@keydown="onKeyDown"
@mousedown="onMouseDown"
@dragstart="onDragStart"
@dragover="onDragOver"
@dragleave="onDragLeave"
@drop="onDrop"
:colspan="columnProp('colspan')"
:rowspan="columnProp('rowspan')"
:aria-sort="ariaSort"
>
<span v-if="resizableColumns && !columnProp('frozen')" class="p-column-resizer" @mousedown="onResizeStart"></span>
<div class="p-column-header-content"> <div class="p-column-header-content">
<component :is="column.children.header" :column="column" v-if="column.children && column.children.header"/> <component v-if="column.children && column.children.header" :is="column.children.header" :column="column" />
<span class="p-column-title" v-if="columnProp('header')">{{columnProp('header')}}</span> <span v-if="columnProp('header')" class="p-column-title">{{ columnProp('header') }}</span>
<span v-if="columnProp('sortable')" :class="sortableColumnIcon"></span> <span v-if="columnProp('sortable')" :class="sortableColumnIcon"></span>
<span v-if="isMultiSorted()" class="p-sortable-column-badge">{{ getBadgeValue() }}</span> <span v-if="isMultiSorted()" class="p-sortable-column-badge">{{ getBadgeValue() }}</span>
<DTHeaderCheckbox :checked="allRowsSelected" @change="onHeaderCheckboxChange" :disabled="empty" v-if="columnProp('selectionMode') ==='multiple' && filterDisplay !== 'row'" /> <DTHeaderCheckbox v-if="columnProp('selectionMode') === 'multiple' && filterDisplay !== 'row'" :checked="allRowsSelected" @change="onHeaderCheckboxChange" :disabled="empty" />
<DTColumnFilter v-if="filterDisplay === 'menu' && column.children && column.children.filter" :field="columnProp('filterField')||columnProp('field')" :type="columnProp('dataType')" display="menu" <DTColumnFilter
:showMenu="columnProp('showFilterMenu')" :filterElement="column.children && column.children.filter" v-if="filterDisplay === 'menu' && column.children && column.children.filter"
:filterHeaderTemplate="column.children && column.children.filterheader" :filterFooterTemplate="column.children && column.children.filterfooter" :field="columnProp('filterField') || columnProp('field')"
:filterClearTemplate="column.children && column.children.filterclear" :filterApplyTemplate="column.children && column.children.filterapply" :type="columnProp('dataType')"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')" :filterMenuStyle="columnProp('filterMenuStyle')" :filterMenuClass="columnProp('filterMenuClass')" display="menu"
:showOperator="columnProp('showFilterOperator')" :showClearButton="columnProp('showClearButton')" :showApplyButton="columnProp('showApplyButton')" :showMenu="columnProp('showFilterMenu')"
:showMatchModes="columnProp('showFilterMatchModes')" :showAddButton="columnProp('showAddButton')" :matchModeOptions="columnProp('filterMatchModeOptions')" :maxConstraints="columnProp('maxConstraints')" :filterElement="column.children && column.children.filter"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)" @constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/> :filterHeaderTemplate="column.children && column.children.filterheader"
:filterFooterTemplate="column.children && column.children.filterfooter"
:filterClearTemplate="column.children && column.children.filterclear"
:filterApplyTemplate="column.children && column.children.filterapply"
:filters="filters"
:filtersStore="filtersStore"
@filter-change="$emit('filter-change', $event)"
@filter-apply="$emit('filter-apply')"
:filterMenuStyle="columnProp('filterMenuStyle')"
:filterMenuClass="columnProp('filterMenuClass')"
:showOperator="columnProp('showFilterOperator')"
:showClearButton="columnProp('showClearButton')"
:showApplyButton="columnProp('showApplyButton')"
:showMatchModes="columnProp('showFilterMatchModes')"
:showAddButton="columnProp('showAddButton')"
:matchModeOptions="columnProp('filterMatchModeOptions')"
:maxConstraints="columnProp('maxConstraints')"
@operator-change="$emit('operator-change', $event)"
@matchmode-change="$emit('matchmode-change', $event)"
@constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)"
@apply-click="$emit('apply-click', $event)"
/>
</div> </div>
</th> </th>
</template> </template>
@ -29,9 +63,24 @@ import ColumnFilter from './ColumnFilter.vue';
export default { export default {
name: 'HeaderCell', name: 'HeaderCell',
emits: ['column-click', 'column-mousedown', 'column-dragstart', 'column-dragover', 'column-dragleave', 'column-drop', emits: [
'column-resizestart', 'checkbox-change', 'filter-change', 'filter-apply', 'column-click',
'operator-change', 'matchmode-change', 'constraint-add', 'constraint-remove', 'filter-clear', 'apply-click'], 'column-mousedown',
'column-dragstart',
'column-dragover',
'column-dragleave',
'column-drop',
'column-resizestart',
'checkbox-change',
'filter-change',
'filter-apply',
'operator-change',
'matchmode-change',
'constraint-add',
'constraint-remove',
'filter-clear',
'apply-click'
],
props: { props: {
column: { column: {
type: Object, type: Object,
@ -97,7 +146,7 @@ export default {
data() { data() {
return { return {
styleObject: {} styleObject: {}
} };
}, },
mounted() { mounted() {
if (this.columnProp('frozen')) { if (this.columnProp('frozen')) {
@ -140,42 +189,48 @@ export default {
this.$emit('column-resizestart', event); this.$emit('column-resizestart', event);
}, },
getMultiSortMetaIndex() { getMultiSortMetaIndex() {
return this.multiSortMeta.findIndex(meta => (meta.field === this.columnProp('field') || meta.field === this.columnProp('sortField'))); return this.multiSortMeta.findIndex((meta) => meta.field === this.columnProp('field') || meta.field === this.columnProp('sortField'));
}, },
getBadgeValue() { getBadgeValue() {
let index = this.getMultiSortMetaIndex(); let index = this.getMultiSortMetaIndex();
return (this.groupRowsBy && this.groupRowsBy === this.groupRowSortField) && index > -1 ? index : index + 1; return this.groupRowsBy && this.groupRowsBy === this.groupRowSortField && index > -1 ? index : index + 1;
}, },
isMultiSorted() { isMultiSorted() {
return this.sortMode === 'multiple' && this.columnProp('sortable') && this.getMultiSortMetaIndex() > -1 return this.sortMode === 'multiple' && this.columnProp('sortable') && this.getMultiSortMetaIndex() > -1;
}, },
isColumnSorted() { isColumnSorted() {
return this.sortMode === 'single' ? (this.sortField && (this.sortField === this.columnProp('field') || this.sortField === this.columnProp('sortField'))) : this.isMultiSorted(); return this.sortMode === 'single' ? this.sortField && (this.sortField === this.columnProp('field') || this.sortField === this.columnProp('sortField')) : this.isMultiSorted();
}, },
updateStickyPosition() { updateStickyPosition() {
if (this.columnProp('frozen')) { if (this.columnProp('frozen')) {
let align = this.columnProp('alignFrozen'); let align = this.columnProp('alignFrozen');
if (align === 'right') { if (align === 'right') {
let right = 0; let right = 0;
let next = this.$el.nextElementSibling; let next = this.$el.nextElementSibling;
if (next) { if (next) {
right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0); right = DomHandler.getOuterWidth(next) + parseFloat(next.style.right || 0);
} }
this.styleObject.right = right + 'px'; this.styleObject.right = right + 'px';
} } else {
else {
let left = 0; let left = 0;
let prev = this.$el.previousElementSibling; let prev = this.$el.previousElementSibling;
if (prev) { if (prev) {
left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0); left = DomHandler.getOuterWidth(prev) + parseFloat(prev.style.left || 0);
} }
this.styleObject.left = left + 'px'; this.styleObject.left = left + 'px';
} }
let filterRow = this.$el.parentElement.nextElementSibling; let filterRow = this.$el.parentElement.nextElementSibling;
if (filterRow) { if (filterRow) {
let index = DomHandler.index(this.$el); let index = DomHandler.index(this.$el);
filterRow.children[index].style.left = this.styleObject.left; filterRow.children[index].style.left = this.styleObject.left;
filterRow.children[index].style.right = this.styleObject.right; filterRow.children[index].style.right = this.styleObject.right;
} }
@ -187,14 +242,18 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return [this.filterColumn ? this.columnProp('filterHeaderClass') : this.columnProp('headerClass'), this.columnProp('class'), { return [
this.filterColumn ? this.columnProp('filterHeaderClass') : this.columnProp('headerClass'),
this.columnProp('class'),
{
'p-sortable-column': this.columnProp('sortable'), 'p-sortable-column': this.columnProp('sortable'),
'p-resizable-column': this.resizableColumns, 'p-resizable-column': this.resizableColumns,
'p-highlight': this.isColumnSorted(), 'p-highlight': this.isColumnSorted(),
'p-filter-column': this.filterColumn, 'p-filter-column': this.filterColumn,
'p-frozen-column': this.columnProp('frozen'), 'p-frozen-column': this.columnProp('frozen'),
'p-reorderable-column': this.reorderableColumns 'p-reorderable-column': this.reorderableColumns
}]; }
];
}, },
containerStyle() { containerStyle() {
let headerStyle = this.filterColumn ? this.columnProp('filterHeaderStyle') : this.columnProp('headerStyle'); let headerStyle = this.filterColumn ? this.columnProp('filterHeaderStyle') : this.columnProp('headerStyle');
@ -209,9 +268,9 @@ export default {
if (this.sortMode === 'single') { if (this.sortMode === 'single') {
sorted = this.sortField && (this.sortField === this.columnProp('field') || this.sortField === this.columnProp('sortField')); sorted = this.sortField && (this.sortField === this.columnProp('field') || this.sortField === this.columnProp('sortField'));
sortOrder = sorted ? this.sortOrder : 0; sortOrder = sorted ? this.sortOrder : 0;
} } else if (this.sortMode === 'multiple') {
else if (this.sortMode === 'multiple') {
let metaIndex = this.getMultiSortMetaIndex(); let metaIndex = this.getMultiSortMetaIndex();
if (metaIndex > -1) { if (metaIndex > -1) {
sorted = true; sorted = true;
sortOrder = this.multiSortMeta[metaIndex].order; sortOrder = this.multiSortMeta[metaIndex].order;
@ -219,7 +278,8 @@ export default {
} }
return [ return [
'p-sortable-column-icon pi pi-fw', { 'p-sortable-column-icon pi pi-fw',
{
'pi-sort-alt': !sorted, 'pi-sort-alt': !sorted,
'pi-sort-amount-up-alt': sorted && sortOrder > 0, 'pi-sort-amount-up-alt': sorted && sortOrder > 0,
'pi-sort-amount-down': sorted && sortOrder < 0 'pi-sort-amount-down': sorted && sortOrder < 0
@ -229,21 +289,18 @@ export default {
ariaSort() { ariaSort() {
if (this.columnProp('sortable')) { if (this.columnProp('sortable')) {
const sortIcon = this.sortableColumnIcon; const sortIcon = this.sortableColumnIcon;
if (sortIcon[1]['pi-sort-amount-down'])
return 'descending'; if (sortIcon[1]['pi-sort-amount-down']) return 'descending';
else if (sortIcon[1]['pi-sort-amount-up-alt']) else if (sortIcon[1]['pi-sort-amount-up-alt']) return 'ascending';
return 'ascending'; else return 'none';
else } else {
return 'none';
}
else {
return null; return null;
} }
} }
}, },
components: { components: {
'DTHeaderCheckbox': HeaderCheckbox, DTHeaderCheckbox: HeaderCheckbox,
'DTColumnFilter': ColumnFilter DTColumnFilter: ColumnFilter
}
} }
};
</script> </script>

View file

@ -1,7 +1,14 @@
<template> <template>
<div :class="['p-checkbox p-component', { 'p-checkbox-focused': focused, 'p-disabled': $attrs.disabled }]" @click="onClick" @keydown.space.prevent="onClick"> <div :class="['p-checkbox p-component', { 'p-checkbox-focused': focused, 'p-disabled': $attrs.disabled }]" @click="onClick" @keydown.space.prevent="onClick">
<div ref="box" :class="['p-checkbox-box p-component', {'p-highlight': checked, 'p-disabled': $attrs.disabled, 'p-focus': focused}]" <div
role="checkbox" :aria-checked="checked" :tabindex="$attrs.disabled ? null : '0'" @focus="onFocus($event)" @blur="onBlur($event)"> ref="box"
:class="['p-checkbox-box p-component', { 'p-highlight': checked, 'p-disabled': $attrs.disabled, 'p-focus': focused }]"
role="checkbox"
:aria-checked="checked"
:tabindex="$attrs.disabled ? null : '0'"
@focus="onFocus($event)"
@blur="onBlur($event)"
>
<span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span> <span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span>
</div> </div>
</div> </div>
@ -37,5 +44,5 @@ export default {
this.focused = false; this.focused = false;
} }
} }
} };
</script> </script>

View file

@ -1,7 +1,15 @@
<template> <template>
<div :class="['p-checkbox p-component', { 'p-checkbox-focused': focused }]" @click.stop.prevent="onClick"> <div :class="['p-checkbox p-component', { 'p-checkbox-focused': focused }]" @click.stop.prevent="onClick">
<div ref="box" :class="['p-checkbox-box p-component', {'p-highlight': checked, 'p-disabled': $attrs.disabled, 'p-focus': focused}]" <div
role="checkbox" :aria-checked="checked" :tabindex="$attrs.disabled ? null : '0'" @keydown.space.prevent="onClick" @focus="onFocus($event)" @blur="onBlur($event)"> ref="box"
:class="['p-checkbox-box p-component', { 'p-highlight': checked, 'p-disabled': $attrs.disabled, 'p-focus': focused }]"
role="checkbox"
:aria-checked="checked"
:tabindex="$attrs.disabled ? null : '0'"
@keydown.space.prevent="onClick"
@focus="onFocus($event)"
@blur="onBlur($event)"
>
<span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span> <span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span>
</div> </div>
</div> </div>
@ -38,5 +46,5 @@ export default {
this.focused = false; this.focused = false;
} }
} }
} };
</script> </script>

View file

@ -38,5 +38,5 @@ export default {
this.focused = false; this.focused = false;
} }
} }
} };
</script> </script>

View file

@ -2,42 +2,75 @@
<tbody :ref="bodyRef" class="p-datatable-tbody" role="rowgroup" :style="bodyStyle"> <tbody :ref="bodyRef" class="p-datatable-tbody" role="rowgroup" :style="bodyStyle">
<template v-if="!empty"> <template v-if="!empty">
<template v-for="(rowData, index) of value" :key="getRowKey(rowData, getRowIndex(index)) + '_subheader'"> <template v-for="(rowData, index) of value" :key="getRowKey(rowData, getRowIndex(index)) + '_subheader'">
<tr class="p-rowgroup-header" :style="rowGroupHeaderStyle" v-if="templates['groupheader'] && rowGroupMode === 'subheader' && shouldRenderRowGroupHeader(value, rowData, getRowIndex(index))" role="row"> <tr v-if="templates['groupheader'] && rowGroupMode === 'subheader' && shouldRenderRowGroupHeader(value, rowData, getRowIndex(index))" class="p-rowgroup-header" :style="rowGroupHeaderStyle" role="row">
<td :colspan="columnsLength - 1"> <td :colspan="columnsLength - 1">
<button class="p-row-toggler p-link" @click="onRowGroupToggle($event, rowData)" v-if="expandableRowGroups" type="button"> <button v-if="expandableRowGroups" class="p-row-toggler p-link" @click="onRowGroupToggle($event, rowData)" type="button">
<span :class="rowGroupTogglerIcon(rowData)"></span> <span :class="rowGroupTogglerIcon(rowData)"></span>
</button> </button>
<component :is="templates['groupheader']" :data="rowData" :index="getRowIndex(index)" /> <component :is="templates['groupheader']" :data="rowData" :index="getRowIndex(index)" />
</td> </td>
</tr> </tr>
<tr :class="getRowClass(rowData)" :style="rowStyle" :key="getRowKey(rowData, getRowIndex(index))" <tr
v-if="expandableRowGroups ? isRowGroupExpanded(rowData) : true" v-if="expandableRowGroups ? isRowGroupExpanded(rowData) : true"
@click="onRowClick($event, rowData, getRowIndex(index))" @dblclick="onRowDblClick($event, rowData, getRowIndex(index))" @contextmenu="onRowRightClick($event, rowData, getRowIndex(index))" @touchend="onRowTouchEnd($event)" @keydown="onRowKeyDown($event, rowData, getRowIndex(index))" :tabindex="selectionMode || contextMenu ? '0' : null" :key="getRowKey(rowData, getRowIndex(index))"
@mousedown="onRowMouseDown($event)" @dragstart="onRowDragStart($event, getRowIndex(index))" @dragover="onRowDragOver($event, getRowIndex(index))" @dragleave="onRowDragLeave($event)" @dragend="onRowDragEnd($event)" @drop="onRowDrop($event)" role="row"> :class="getRowClass(rowData)"
:style="rowStyle"
@click="onRowClick($event, rowData, getRowIndex(index))"
@dblclick="onRowDblClick($event, rowData, getRowIndex(index))"
@contextmenu="onRowRightClick($event, rowData, getRowIndex(index))"
@touchend="onRowTouchEnd($event)"
@keydown="onRowKeyDown($event, rowData, getRowIndex(index))"
:tabindex="selectionMode || contextMenu ? '0' : null"
@mousedown="onRowMouseDown($event)"
@dragstart="onRowDragStart($event, getRowIndex(index))"
@dragover="onRowDragOver($event, getRowIndex(index))"
@dragleave="onRowDragLeave($event)"
@dragend="onRowDragEnd($event)"
@drop="onRowDrop($event)"
role="row"
>
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i"> <template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<DTBodyCell v-if="shouldRenderBodyCell(value, col, getRowIndex(index))" :rowData="rowData" :column="col" :rowIndex="getRowIndex(index)" :index="i" :selected="isSelected(rowData)" <DTBodyCell
:rowTogglerIcon="columnProp(col,'expander') ? rowTogglerIcon(rowData): null" :frozenRow="frozenRow" v-if="shouldRenderBodyCell(value, col, getRowIndex(index))"
:rowData="rowData"
:column="col"
:rowIndex="getRowIndex(index)"
:index="i"
:selected="isSelected(rowData)"
:rowTogglerIcon="columnProp(col, 'expander') ? rowTogglerIcon(rowData) : null"
:frozenRow="frozenRow"
:rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(value, col, getRowIndex(index)) : null" :rowspan="rowGroupMode === 'rowspan' ? calculateRowGroupSize(value, col, getRowIndex(index)) : null"
:editMode="editMode" :editing="editMode === 'row' && isRowEditing(rowData)" :responsiveLayout="responsiveLayout" :editMode="editMode"
@radio-change="onRadioChange($event)" @checkbox-change="onCheckboxChange($event)" @row-toggle="onRowToggle($event)" :editing="editMode === 'row' && isRowEditing(rowData)"
@cell-edit-init="onCellEditInit($event)" @cell-edit-complete="onCellEditComplete($event)" @cell-edit-cancel="onCellEditCancel($event)" :responsiveLayout="responsiveLayout"
@row-edit-init="onRowEditInit($event)" @row-edit-save="onRowEditSave($event)" @row-edit-cancel="onRowEditCancel($event)" @radio-change="onRadioChange($event)"
:editingMeta="editingMeta" @editing-meta-change="onEditingMetaChange" :virtualScrollerContentProps="virtualScrollerContentProps"/> @checkbox-change="onCheckboxChange($event)"
@row-toggle="onRowToggle($event)"
@cell-edit-init="onCellEditInit($event)"
@cell-edit-complete="onCellEditComplete($event)"
@cell-edit-cancel="onCellEditCancel($event)"
@row-edit-init="onRowEditInit($event)"
@row-edit-save="onRowEditSave($event)"
@row-edit-cancel="onRowEditCancel($event)"
:editingMeta="editingMeta"
@editing-meta-change="onEditingMetaChange"
:virtualScrollerContentProps="virtualScrollerContentProps"
/>
</template> </template>
</tr> </tr>
<tr class="p-datatable-row-expansion" v-if="templates['expansion'] && expandedRows && isRowExpanded(rowData)" :key="getRowKey(rowData, getRowIndex(index)) + '_expansion'" role="row"> <tr v-if="templates['expansion'] && expandedRows && isRowExpanded(rowData)" :key="getRowKey(rowData, getRowIndex(index)) + '_expansion'" class="p-datatable-row-expansion" role="row">
<td :colspan="columnsLength"> <td :colspan="columnsLength">
<component :is="templates['expansion']" :data="rowData" :index="getRowIndex(index)" /> <component :is="templates['expansion']" :data="rowData" :index="getRowIndex(index)" />
</td> </td>
</tr> </tr>
<tr class="p-rowgroup-footer" v-if="templates['groupfooter'] && rowGroupMode === 'subheader' && shouldRenderRowGroupFooter(value, rowData, getRowIndex(index))" :key="getRowKey(rowData, getRowIndex(index)) + '_subfooter'" role="row"> <tr v-if="templates['groupfooter'] && rowGroupMode === 'subheader' && shouldRenderRowGroupFooter(value, rowData, getRowIndex(index))" :key="getRowKey(rowData, getRowIndex(index)) + '_subfooter'" class="p-rowgroup-footer" role="row">
<component :is="templates['groupfooter']" :data="rowData" :index="getRowIndex(index)" /> <component :is="templates['groupfooter']" :data="rowData" :index="getRowIndex(index)" />
</tr> </tr>
</template> </template>
</template> </template>
<tr v-else class="p-datatable-emptymessage" role="row"> <tr v-else class="p-datatable-emptymessage" role="row">
<td :colspan="columnsLength"> <td :colspan="columnsLength">
<component :is="templates.empty" v-if="templates.empty" /> <component v-if="templates.empty" :is="templates.empty" />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -49,10 +82,30 @@ import BodyCell from './BodyCell.vue';
export default { export default {
name: 'TableBody', name: 'TableBody',
emits: ['rowgroup-toggle', 'row-click', 'row-dblclick', 'row-rightclick', 'row-touchend', 'row-keydown', 'row-mousedown', emits: [
'row-dragstart', 'row-dragover', 'row-dragleave', 'row-dragend', 'row-drop', 'row-toggle', 'rowgroup-toggle',
'radio-change', 'checkbox-change', 'cell-edit-init', 'cell-edit-complete', 'cell-edit-cancel', 'row-click',
'row-edit-init', 'row-edit-save', 'row-edit-cancel', 'editing-meta-change'], 'row-dblclick',
'row-rightclick',
'row-touchend',
'row-keydown',
'row-mousedown',
'row-dragstart',
'row-dragover',
'row-dragleave',
'row-dragend',
'row-drop',
'row-toggle',
'radio-change',
'checkbox-change',
'cell-edit-init',
'cell-edit-complete',
'cell-edit-cancel',
'row-edit-init',
'row-edit-save',
'row-edit-cancel',
'editing-meta-change'
],
props: { props: {
value: { value: {
type: Array, type: Array,
@ -175,6 +228,11 @@ export default {
default: false default: false
} }
}, },
data() {
return {
rowGroupHeaderStyleObject: {}
};
},
watch: { watch: {
virtualScrollerContentProps(newValue, oldValue) { virtualScrollerContentProps(newValue, oldValue) {
if (!this.isVirtualScrollerDisabled && this.getVirtualScrollerProp('vertical') && this.getVirtualScrollerProp('itemSize', oldValue) !== this.getVirtualScrollerProp('itemSize', newValue)) { if (!this.isVirtualScrollerDisabled && this.getVirtualScrollerProp('vertical') && this.getVirtualScrollerProp('itemSize', oldValue) !== this.getVirtualScrollerProp('itemSize', newValue)) {
@ -204,11 +262,6 @@ export default {
this.updateFrozenRowGroupHeaderStickyPosition(); this.updateFrozenRowGroupHeaderStickyPosition();
} }
}, },
data() {
return {
rowGroupHeaderStyleObject: {}
}
},
methods: { methods: {
columnProp(col, prop) { columnProp(col, prop) {
return ObjectUtils.getVNodeProp(col, prop); return ObjectUtils.getVNodeProp(col, prop);
@ -216,11 +269,12 @@ export default {
shouldRenderRowGroupHeader(value, rowData, i) { shouldRenderRowGroupHeader(value, rowData, i) {
let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy); let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
let prevRowData = value[i - 1]; let prevRowData = value[i - 1];
if (prevRowData) { if (prevRowData) {
let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.groupRowsBy); let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.groupRowsBy);
return currentRowFieldData !== previousRowFieldData; return currentRowFieldData !== previousRowFieldData;
} } else {
else {
return true; return true;
} }
}, },
@ -229,10 +283,12 @@ export default {
}, },
getRowIndex(index) { getRowIndex(index) {
const getItemOptions = this.getVirtualScrollerProp('getItemOptions'); const getItemOptions = this.getVirtualScrollerProp('getItemOptions');
return getItemOptions ? getItemOptions(index).index : index; return getItemOptions ? getItemOptions(index).index : index;
}, },
getRowClass(rowData) { getRowClass(rowData) {
let rowStyleClass = []; let rowStyleClass = [];
if (this.selectionMode) { if (this.selectionMode) {
rowStyleClass.push('p-selectable-row'); rowStyleClass.push('p-selectable-row');
} }
@ -262,15 +318,15 @@ export default {
shouldRenderRowGroupFooter(value, rowData, i) { shouldRenderRowGroupFooter(value, rowData, i) {
if (this.expandableRowGroups && !this.isRowGroupExpanded(rowData)) { if (this.expandableRowGroups && !this.isRowGroupExpanded(rowData)) {
return false; return false;
} } else {
else {
let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy); let currentRowFieldData = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
let nextRowData = value[i + 1]; let nextRowData = value[i + 1];
if (nextRowData) { if (nextRowData) {
let nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.groupRowsBy); let nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.groupRowsBy);
return currentRowFieldData !== nextRowFieldData; return currentRowFieldData !== nextRowFieldData;
} } else {
else {
return true; return true;
} }
} }
@ -279,25 +335,23 @@ export default {
if (this.rowGroupMode) { if (this.rowGroupMode) {
if (this.rowGroupMode === 'subheader') { if (this.rowGroupMode === 'subheader') {
return this.groupRowsBy !== this.columnProp(column, 'field'); return this.groupRowsBy !== this.columnProp(column, 'field');
} } else if (this.rowGroupMode === 'rowspan') {
else if (this.rowGroupMode === 'rowspan') {
if (this.isGrouped(column)) { if (this.isGrouped(column)) {
let prevRowData = value[i - 1]; let prevRowData = value[i - 1];
if (prevRowData) { if (prevRowData) {
let currentRowFieldData = ObjectUtils.resolveFieldData(value[i], this.columnProp(column, 'field')); let currentRowFieldData = ObjectUtils.resolveFieldData(value[i], this.columnProp(column, 'field'));
let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.columnProp(column, 'field')); let previousRowFieldData = ObjectUtils.resolveFieldData(prevRowData, this.columnProp(column, 'field'));
return currentRowFieldData !== previousRowFieldData; return currentRowFieldData !== previousRowFieldData;
} else {
return true;
} }
else { } else {
return true; return true;
} }
} }
else { } else {
return true;
}
}
}
else {
return !this.columnProp(column, 'hidden'); return !this.columnProp(column, 'hidden');
} }
}, },
@ -310,55 +364,49 @@ export default {
while (currentRowFieldData === nextRowFieldData) { while (currentRowFieldData === nextRowFieldData) {
groupRowSpan++; groupRowSpan++;
let nextRowData = value[++index]; let nextRowData = value[++index];
if (nextRowData) { if (nextRowData) {
nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.columnProp(column, 'field')); nextRowFieldData = ObjectUtils.resolveFieldData(nextRowData, this.columnProp(column, 'field'));
} } else {
else {
break; break;
} }
} }
return groupRowSpan === 1 ? null : groupRowSpan; return groupRowSpan === 1 ? null : groupRowSpan;
} } else {
else {
return null; return null;
} }
}, },
rowTogglerIcon(rowData) { rowTogglerIcon(rowData) {
const icon = this.isRowExpanded(rowData) ? this.expandedRowIcon : this.collapsedRowIcon; const icon = this.isRowExpanded(rowData) ? this.expandedRowIcon : this.collapsedRowIcon;
return ['p-row-toggler-icon pi', icon]; return ['p-row-toggler-icon pi', icon];
}, },
rowGroupTogglerIcon(rowData) { rowGroupTogglerIcon(rowData) {
const icon = this.isRowGroupExpanded(rowData) ? this.expandedRowIcon : this.collapsedRowIcon; const icon = this.isRowGroupExpanded(rowData) ? this.expandedRowIcon : this.collapsedRowIcon;
return ['p-row-toggler-icon pi', icon]; return ['p-row-toggler-icon pi', icon];
}, },
isGrouped(column) { isGrouped(column) {
if (this.groupRowsBy && this.columnProp(column, 'field')) { if (this.groupRowsBy && this.columnProp(column, 'field')) {
if (Array.isArray(this.groupRowsBy)) if (Array.isArray(this.groupRowsBy)) return this.groupRowsBy.indexOf(column.props.field) > -1;
return this.groupRowsBy.indexOf(column.props.field) > -1; else return this.groupRowsBy === column.props.field;
else } else {
return this.groupRowsBy === column.props.field;
}
else {
return false; return false;
} }
}, },
isRowEditing(rowData) { isRowEditing(rowData) {
if (rowData && this.editingRows) { if (rowData && this.editingRows) {
if (this.dataKey) if (this.dataKey) return this.editingRowKeys ? this.editingRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
return this.editingRowKeys ? this.editingRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false; else return this.findIndex(rowData, this.editingRows) > -1;
else
return this.findIndex(rowData, this.editingRows) > -1;
} }
return false; return false;
}, },
isRowExpanded(rowData) { isRowExpanded(rowData) {
if (rowData && this.expandedRows) { if (rowData && this.expandedRows) {
if (this.dataKey) if (this.dataKey) return this.expandedRowKeys ? this.expandedRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
return this.expandedRowKeys ? this.expandedRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false; else return this.findIndex(rowData, this.expandedRows) > -1;
else
return this.findIndex(rowData, this.expandedRows) > -1;
} }
return false; return false;
@ -366,20 +414,19 @@ export default {
isRowGroupExpanded(rowData) { isRowGroupExpanded(rowData) {
if (this.expandableRowGroups && this.expandedRowGroups) { if (this.expandableRowGroups && this.expandedRowGroups) {
let groupFieldValue = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy); let groupFieldValue = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);
return this.expandedRowGroups.indexOf(groupFieldValue) > -1; return this.expandedRowGroups.indexOf(groupFieldValue) > -1;
} }
return false; return false;
}, },
isSelected(rowData) { isSelected(rowData) {
if (rowData && this.selection) { if (rowData && this.selection) {
if (this.dataKey) { if (this.dataKey) {
return this.selectionKeys ? this.selectionKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false; return this.selectionKeys ? this.selectionKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
} } else {
else { if (this.selection instanceof Array) return this.findIndexInSelection(rowData) > -1;
if (this.selection instanceof Array) else return this.equals(rowData, this.selection);
return this.findIndexInSelection(rowData) > -1;
else
return this.equals(rowData, this.selection);
} }
} }
@ -397,6 +444,7 @@ export default {
}, },
findIndex(rowData, collection) { findIndex(rowData, collection) {
let index = -1; let index = -1;
if (collection && collection.length) { if (collection && collection.length) {
for (let i = 0; i < collection.length; i++) { for (let i = 0; i < collection.length; i++) {
if (this.equals(rowData, collection[i])) { if (this.equals(rowData, collection[i])) {
@ -409,7 +457,7 @@ export default {
return index; return index;
}, },
equals(data1, data2) { equals(data1, data2) {
return this.compareSelectionBy === 'equals' ? (data1 === data2) : ObjectUtils.equals(data1, data2, this.dataKey); return this.compareSelectionBy === 'equals' ? data1 === data2 : ObjectUtils.equals(data1, data2, this.dataKey);
}, },
onRowGroupToggle(event, data) { onRowGroupToggle(event, data) {
this.$emit('rowgroup-toggle', { originalEvent: event, data: data }); this.$emit('rowgroup-toggle', { originalEvent: event, data: data });
@ -482,19 +530,23 @@ export default {
}, },
updateFrozenRowGroupHeaderStickyPosition() { updateFrozenRowGroupHeaderStickyPosition() {
let tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling); let tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling);
this.rowGroupHeaderStyleObject.top = tableHeaderHeight + 'px'
this.rowGroupHeaderStyleObject.top = tableHeaderHeight + 'px';
}, },
updateVirtualScrollerPosition() { updateVirtualScrollerPosition() {
const tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling); const tableHeaderHeight = DomHandler.getOuterHeight(this.$el.previousElementSibling);
this.$el.style.top = (this.$el.style.top || 0) + tableHeaderHeight + 'px'; this.$el.style.top = (this.$el.style.top || 0) + tableHeaderHeight + 'px';
}, },
getVirtualScrollerProp(option, options) { getVirtualScrollerProp(option, options) {
options = options || this.virtualScrollerContentProps; options = options || this.virtualScrollerContentProps;
return options ? options[option] : null; return options ? options[option] : null;
}, },
bodyRef(el) { bodyRef(el) {
// For VirtualScroller // For VirtualScroller
const contentRef = this.getVirtualScrollerProp('contentRef'); const contentRef = this.getVirtualScrollerProp('contentRef');
contentRef && contentRef(el); contentRef && contentRef(el);
} }
}, },
@ -502,7 +554,7 @@ export default {
columnsLength() { columnsLength() {
let hiddenColLength = 0; let hiddenColLength = 0;
this.columns.forEach(column => { this.columns.forEach((column) => {
if (this.columnProp(column, 'hidden')) hiddenColLength++; if (this.columnProp(column, 'hidden')) hiddenColLength++;
}); });
@ -520,7 +572,7 @@ export default {
} }
}, },
components: { components: {
'DTBodyCell': BodyCell DTBodyCell: BodyCell
}
} }
};
</script> </script>

View file

@ -1,14 +1,14 @@
<template> <template>
<tfoot class="p-datatable-tfoot" v-if="hasFooter" role="rowgroup"> <tfoot v-if="hasFooter" class="p-datatable-tfoot" role="rowgroup">
<tr v-if="!columnGroup" role="row"> <tr v-if="!columnGroup" role="row">
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i"> <template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<DTFooterCell :column="col" v-if="!columnProp(col,'hidden')"/> <DTFooterCell v-if="!columnProp(col, 'hidden')" :column="col" />
</template> </template>
</tr> </tr>
<template v-else> <template v-else>
<tr v-for="(row, i) of getFooterRows()" :key="i" role="row"> <tr v-for="(row, i) of getFooterRows()" :key="i" role="row">
<template v-for="(col, j) of getFooterColumns(row)" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || j"> <template v-for="(col, j) of getFooterColumns(row)" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || j">
<DTFooterCell :column="col" v-if="!columnProp(col,'hidden')"/> <DTFooterCell v-if="!columnProp(col, 'hidden')" :column="col" />
</template> </template>
</tr> </tr>
</template> </template>
@ -29,7 +29,7 @@ export default {
columns: { columns: {
type: null, type: null,
default: null default: null
}, }
}, },
methods: { methods: {
columnProp(col, prop) { columnProp(col, prop) {
@ -39,12 +39,12 @@ export default {
let rows = []; let rows = [];
let columnGroup = this.columnGroup; let columnGroup = this.columnGroup;
if (columnGroup.children && columnGroup.children.default) { if (columnGroup.children && columnGroup.children.default) {
for (let child of columnGroup.children.default()) { for (let child of columnGroup.children.default()) {
if (child.type.name === 'Row') { if (child.type.name === 'Row') {
rows.push(child); rows.push(child);
} } else if (child.children && child.children instanceof Array) {
else if (child.children && child.children instanceof Array) {
rows = child.children; rows = child.children;
} }
} }
@ -56,11 +56,9 @@ export default {
let cols = []; let cols = [];
if (row.children && row.children.default) { if (row.children && row.children.default) {
row.children.default().forEach(child => { row.children.default().forEach((child) => {
if (child.children && child.children instanceof Array) if (child.children && child.children instanceof Array) cols = [...cols, ...child.children];
cols = [...cols, ...child.children]; else if (child.type.name === 'Column') cols.push(child);
else if (child.type.name === 'Column')
cols.push(child);
}); });
return cols; return cols;
@ -73,8 +71,7 @@ export default {
if (this.columnGroup) { if (this.columnGroup) {
hasFooter = true; hasFooter = true;
} } else if (this.columns) {
else if (this.columns) {
for (let col of this.columns) { for (let col of this.columns) {
if (this.columnProp(col, 'footer') || (col.children && col.children.footer)) { if (this.columnProp(col, 'footer') || (col.children && col.children.footer)) {
hasFooter = true; hasFooter = true;
@ -87,7 +84,7 @@ export default {
} }
}, },
components: { components: {
'DTFooterCell': FooterCell DTFooterCell: FooterCell
}
} }
};
</script> </script>

View file

@ -3,30 +3,74 @@
<template v-if="!columnGroup"> <template v-if="!columnGroup">
<tr role="row"> <tr role="row">
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i"> <template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<DTHeaderCell v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field')))" :column="col" <DTHeaderCell
@column-click="$emit('column-click', $event)" @column-mousedown="$emit('column-mousedown', $event)" v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || groupRowsBy !== columnProp(col, 'field'))"
@column-dragstart="$emit('column-dragstart', $event)" @column-dragover="$emit('column-dragover', $event)" @column-dragleave="$emit('column-dragleave', $event)" @column-drop="$emit('column-drop', $event)" :column="col"
:groupRowsBy="groupRowsBy" :groupRowSortField="groupRowSortField" :reorderableColumns="reorderableColumns" :resizableColumns="resizableColumns" @column-resizestart="$emit('column-resizestart', $event)" @column-click="$emit('column-click', $event)"
:sortMode="sortMode" :sortField="sortField" :sortOrder="sortOrder" :multiSortMeta="multiSortMeta" @column-mousedown="$emit('column-mousedown', $event)"
:allRowsSelected="allRowsSelected" :empty="empty" @checkbox-change="$emit('checkbox-change', $event)" @column-dragstart="$emit('column-dragstart', $event)"
:filters="filters" :filterDisplay="filterDisplay" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')" @column-dragover="$emit('column-dragover', $event)"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)" @column-dragleave="$emit('column-dragleave', $event)"
@constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/> @column-drop="$emit('column-drop', $event)"
:groupRowsBy="groupRowsBy"
:groupRowSortField="groupRowSortField"
:reorderableColumns="reorderableColumns"
:resizableColumns="resizableColumns"
@column-resizestart="$emit('column-resizestart', $event)"
:sortMode="sortMode"
:sortField="sortField"
:sortOrder="sortOrder"
:multiSortMeta="multiSortMeta"
:allRowsSelected="allRowsSelected"
:empty="empty"
@checkbox-change="$emit('checkbox-change', $event)"
:filters="filters"
:filterDisplay="filterDisplay"
:filtersStore="filtersStore"
@filter-change="$emit('filter-change', $event)"
@filter-apply="$emit('filter-apply')"
@operator-change="$emit('operator-change', $event)"
@matchmode-change="$emit('matchmode-change', $event)"
@constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)"
@apply-click="$emit('apply-click', $event)"
/>
</template> </template>
</tr> </tr>
<tr v-if="filterDisplay === 'row'" role="row"> <tr v-if="filterDisplay === 'row'" role="row">
<template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i"> <template v-for="(col, i) of columns" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || i">
<th :style="getFilterColumnHeaderStyle(col)" :class="getFilterColumnHeaderClass(col)" v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field')))"> <th v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || groupRowsBy !== columnProp(col, 'field'))" :style="getFilterColumnHeaderStyle(col)" :class="getFilterColumnHeaderClass(col)">
<DTHeaderCheckbox :checked="allRowsSelected" @change="$emit('checkbox-change', $event)" :disabled="empty" v-if="columnProp(col, 'selectionMode') ==='multiple'" /> <DTHeaderCheckbox v-if="columnProp(col, 'selectionMode') === 'multiple'" :checked="allRowsSelected" @change="$emit('checkbox-change', $event)" :disabled="empty" />
<DTColumnFilter v-if="col.children && col.children.filter" :field="columnProp(col,'filterField')||columnProp(col,'field')" :type="columnProp(col,'dataType')" display="row" <DTColumnFilter
:showMenu="columnProp(col,'showFilterMenu')" :filterElement="col.children && col.children.filter" v-if="col.children && col.children.filter"
:filterHeaderTemplate="col.children && col.children.filterheader" :filterFooterTemplate="col.children && col.children.filterfooter" :field="columnProp(col, 'filterField') || columnProp(col, 'field')"
:filterClearTemplate="col.children && col.children.filterclear" :filterApplyTemplate="col.children && col.children.filterapply" :type="columnProp(col, 'dataType')"
:filters="filters" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')" :filterMenuStyle="columnProp(col,'filterMenuStyle')" :filterMenuClass="columnProp(col,'filterMenuClass')" display="row"
:showOperator="columnProp(col,'showFilterOperator')" :showClearButton="columnProp(col,'showClearButton')" :showApplyButton="columnProp(col,'showApplyButton')" :showMenu="columnProp(col, 'showFilterMenu')"
:showMatchModes="columnProp(col,'showFilterMatchModes')" :showAddButton="columnProp(col,'showAddButton')" :matchModeOptions="columnProp(col,'filterMatchModeOptions')" :maxConstraints="columnProp(col,'maxConstraints')" :filterElement="col.children && col.children.filter"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" :filterHeaderTemplate="col.children && col.children.filterheader"
@constraint-add="$emit('constraint-add', $event)" @constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/> :filterFooterTemplate="col.children && col.children.filterfooter"
:filterClearTemplate="col.children && col.children.filterclear"
:filterApplyTemplate="col.children && col.children.filterapply"
:filters="filters"
:filtersStore="filtersStore"
@filter-change="$emit('filter-change', $event)"
@filter-apply="$emit('filter-apply')"
:filterMenuStyle="columnProp(col, 'filterMenuStyle')"
:filterMenuClass="columnProp(col, 'filterMenuClass')"
:showOperator="columnProp(col, 'showFilterOperator')"
:showClearButton="columnProp(col, 'showClearButton')"
:showApplyButton="columnProp(col, 'showApplyButton')"
:showMatchModes="columnProp(col, 'showFilterMatchModes')"
:showAddButton="columnProp(col, 'showAddButton')"
:matchModeOptions="columnProp(col, 'filterMatchModeOptions')"
:maxConstraints="columnProp(col, 'maxConstraints')"
@operator-change="$emit('operator-change', $event)"
@matchmode-change="$emit('matchmode-change', $event)"
@constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)"
@apply-click="$emit('apply-click', $event)"
/>
</th> </th>
</template> </template>
</tr> </tr>
@ -34,13 +78,31 @@
<template v-else> <template v-else>
<tr v-for="(row, i) of getHeaderRows()" :key="i" role="row"> <tr v-for="(row, i) of getHeaderRows()" :key="i" role="row">
<template v-for="(col, j) of getHeaderColumns(row)" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || j"> <template v-for="(col, j) of getHeaderColumns(row)" :key="columnProp(col, 'columnKey') || columnProp(col, 'field') || j">
<DTHeaderCell v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || (groupRowsBy !== columnProp(col, 'field'))) && (typeof col.children !== 'string')" :column="col" <DTHeaderCell
@column-click="$emit('column-click', $event)" @column-mousedown="$emit('column-mousedown', $event)" v-if="!columnProp(col, 'hidden') && (rowGroupMode !== 'subheader' || groupRowsBy !== columnProp(col, 'field')) && typeof col.children !== 'string'"
:groupRowsBy="groupRowsBy" :groupRowSortField="groupRowSortField" :sortMode="sortMode" :sortField="sortField" :sortOrder="sortOrder" :multiSortMeta="multiSortMeta" :column="col"
:allRowsSelected="allRowsSelected" :empty="empty" @checkbox-change="$emit('checkbox-change', $event)" @column-click="$emit('column-click', $event)"
:filters="filters" :filterDisplay="filterDisplay" :filtersStore="filtersStore" @filter-change="$emit('filter-change', $event)" @filter-apply="$emit('filter-apply')" @column-mousedown="$emit('column-mousedown', $event)"
@operator-change="$emit('operator-change',$event)" @matchmode-change="$emit('matchmode-change', $event)" @constraint-add="$emit('constraint-add', $event)" :groupRowsBy="groupRowsBy"
@constraint-remove="$emit('constraint-remove', $event)" @apply-click="$emit('apply-click',$event)"/> :groupRowSortField="groupRowSortField"
:sortMode="sortMode"
:sortField="sortField"
:sortOrder="sortOrder"
:multiSortMeta="multiSortMeta"
:allRowsSelected="allRowsSelected"
:empty="empty"
@checkbox-change="$emit('checkbox-change', $event)"
:filters="filters"
:filterDisplay="filterDisplay"
:filtersStore="filtersStore"
@filter-change="$emit('filter-change', $event)"
@filter-apply="$emit('filter-apply')"
@operator-change="$emit('operator-change', $event)"
@matchmode-change="$emit('matchmode-change', $event)"
@constraint-add="$emit('constraint-add', $event)"
@constraint-remove="$emit('constraint-remove', $event)"
@apply-click="$emit('apply-click', $event)"
/>
</template> </template>
</tr> </tr>
</template> </template>
@ -55,9 +117,24 @@ import {ObjectUtils} from 'primevue/utils';
export default { export default {
name: 'TableHeader', name: 'TableHeader',
emits: ['column-click', 'column-mousedown', 'column-dragstart', 'column-dragover', 'column-dragleave', 'column-drop', emits: [
'column-resizestart', 'checkbox-change', 'filter-change', 'filter-apply', 'column-click',
'operator-change', 'matchmode-change', 'constraint-add', 'constraint-remove', 'filter-clear', 'apply-click'], 'column-mousedown',
'column-dragstart',
'column-dragover',
'column-dragleave',
'column-drop',
'column-resizestart',
'checkbox-change',
'filter-change',
'filter-apply',
'operator-change',
'matchmode-change',
'constraint-add',
'constraint-remove',
'filter-clear',
'apply-click'
],
props: { props: {
columnGroup: { columnGroup: {
type: null, type: null,
@ -129,9 +206,14 @@ export default {
return ObjectUtils.getVNodeProp(col, prop); return ObjectUtils.getVNodeProp(col, prop);
}, },
getFilterColumnHeaderClass(column) { getFilterColumnHeaderClass(column) {
return ['p-filter-column', this.columnProp(column, 'filterHeaderClass'), this.columnProp(column, 'class'), { return [
'p-filter-column',
this.columnProp(column, 'filterHeaderClass'),
this.columnProp(column, 'class'),
{
'p-frozen-column': this.columnProp(column, 'frozen') 'p-frozen-column': this.columnProp(column, 'frozen')
}]; }
];
}, },
getFilterColumnHeaderStyle(column) { getFilterColumnHeaderStyle(column) {
return [this.columnProp(column, 'filterHeaderStyle'), this.columnProp(column, 'style')]; return [this.columnProp(column, 'filterHeaderStyle'), this.columnProp(column, 'style')];
@ -140,12 +222,12 @@ export default {
let rows = []; let rows = [];
let columnGroup = this.columnGroup; let columnGroup = this.columnGroup;
if (columnGroup.children && columnGroup.children.default) { if (columnGroup.children && columnGroup.children.default) {
for (let child of columnGroup.children.default()) { for (let child of columnGroup.children.default()) {
if (child.type.name === 'Row') { if (child.type.name === 'Row') {
rows.push(child); rows.push(child);
} } else if (child.children && child.children instanceof Array) {
else if (child.children && child.children instanceof Array) {
rows = child.children; rows = child.children;
} }
} }
@ -157,11 +239,9 @@ export default {
let cols = []; let cols = [];
if (row.children && row.children.default) { if (row.children && row.children.default) {
row.children.default().forEach(child => { row.children.default().forEach((child) => {
if (child.children && child.children instanceof Array) if (child.children && child.children instanceof Array) cols = [...cols, ...child.children];
cols = [...cols, ...child.children]; else if (child.type.name === 'Column') cols.push(child);
else if (child.type.name === 'Column')
cols.push(child);
}); });
return cols; return cols;
@ -169,9 +249,9 @@ export default {
} }
}, },
components: { components: {
'DTHeaderCell': HeaderCell, DTHeaderCell: HeaderCell,
'DTHeaderCheckbox': HeaderCheckbox, DTHeaderCheckbox: HeaderCheckbox,
'DTColumnFilter': ColumnFilter DTColumnFilter: ColumnFilter
}
} }
};
</script> </script>

View file

@ -2,7 +2,7 @@
<tbody class="p-datatable-tbody"> <tbody class="p-datatable-tbody">
<tr v-for="n in rows" :key="n"> <tr v-for="n in rows" :key="n">
<td v-for="(col, i) of columns" :key="col.props.columnKey || col.props.field || i"> <td v-for="(col, i) of columns" :key="col.props.columnKey || col.props.field || i">
<component :is="col.children.loading" :column="col" :index="i" v-if="col.children && col.children.loading" /> <component v-if="col.children && col.children.loading" :is="col.children.loading" :column="col" :index="i" />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -21,5 +21,5 @@ export default {
default: null default: null
} }
} }
} };
</script> </script>

View file

@ -185,14 +185,14 @@ export declare type DataViewEmits = {
* Callback to invoke when page changes, the event object contains information about the new state. * Callback to invoke when page changes, the event object contains information about the new state.
* @param {DataViewPageEvent} event - Custom page event. * @param {DataViewPageEvent} event - Custom page event.
*/ */
'page': (event: DataViewPageEvent) => void; page: (event: DataViewPageEvent) => void;
} };
declare class DataView extends ClassComponent<DataViewProps, DataViewSlots, DataViewEmits> {} declare class DataView extends ClassComponent<DataViewProps, DataViewSlots, DataViewEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
DataView: GlobalComponentConstructor<DataView> DataView: GlobalComponentConstructor<DataView>;
} }
} }
@ -206,7 +206,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [DataView](https://www.primefaces.org/primevue/showcase/#/dataview) * - [DataView](https://www.primefaces.org/primevue/dataview)
* *
*/ */
export default DataView; export default DataView;

View file

@ -7,40 +7,40 @@ describe('DataView.vue', () => {
props: { props: {
value: [ value: [
{ {
"id": "1000", id: '1000',
"code": "f230fh0g3", code: 'f230fh0g3',
"name": "Bamboo Watch", name: 'Bamboo Watch',
"description": "Product Description", description: 'Product Description',
"image": "bamboo-watch.jpg", image: 'bamboo-watch.jpg',
"price": 65, price: 65,
"category": "Accessories", category: 'Accessories',
"quantity": 24, quantity: 24,
"inventoryStatus": "INSTOCK", inventoryStatus: 'INSTOCK',
"rating": 5 rating: 5
}, },
{ {
"id": "1001", id: '1001',
"code": "nvklal433", code: 'nvklal433',
"name": "Black Watch", name: 'Black Watch',
"description": "Product Description", description: 'Product Description',
"image": "black-watch.jpg", image: 'black-watch.jpg',
"price": 72, price: 72,
"category": "Accessories", category: 'Accessories',
"quantity": 61, quantity: 61,
"inventoryStatus": "INSTOCK", inventoryStatus: 'INSTOCK',
"rating": 4 rating: 4
}, },
{ {
"id": "1002", id: '1002',
"code": "zz21cz3c1", code: 'zz21cz3c1',
"name": "Blue Band", name: 'Blue Band',
"description": "Product Description", description: 'Product Description',
"image": "blue-band.jpg", image: 'blue-band.jpg',
"price": 79, price: 79,
"category": "Fitness", category: 'Fitness',
"quantity": 2, quantity: 2,
"inventoryStatus": "LOWSTOCK", inventoryStatus: 'LOWSTOCK',
"rating": 3 rating: 3
} }
], ],
layout: 'grid', layout: 'grid',

View file

@ -1,14 +1,25 @@
<template> <template>
<div :class="containerClass"> <div :class="containerClass">
<div class="p-dataview-header" v-if="$slots.header"> <div v-if="$slots.header" class="p-dataview-header">
<slot name="header"></slot> <slot name="header"></slot>
</div> </div>
<DVPaginator v-if="paginatorTop" :rows="d_rows" :first="d_first" :totalRecords="getTotalRecords" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions" <DVPaginator
:currentPageReportTemplate="currentPageReportTemplate" :class="{'p-paginator-top': paginatorTop}" :alwaysShow="alwaysShowPaginator" @page="onPage($event)"> v-if="paginatorTop"
<template #start v-if="$slots.paginatorstart"> :rows="d_rows"
:first="d_first"
:totalRecords="getTotalRecords"
:pageLinkSize="pageLinkSize"
:template="paginatorTemplate"
:rowsPerPageOptions="rowsPerPageOptions"
:currentPageReportTemplate="currentPageReportTemplate"
:class="{ 'p-paginator-top': paginatorTop }"
:alwaysShow="alwaysShowPaginator"
@page="onPage($event)"
>
<template v-if="$slots.paginatorstart" #start>
<slot name="paginatorstart"></slot> <slot name="paginatorstart"></slot>
</template> </template>
<template #end v-if="$slots.paginatorend"> <template v-if="$slots.paginatorend" #end>
<slot name="paginatorend"></slot> <slot name="paginatorend"></slot>
</template> </template>
</DVPaginator> </DVPaginator>
@ -25,16 +36,27 @@
</div> </div>
</div> </div>
</div> </div>
<DVPaginator v-if="paginatorBottom" :rows="d_rows" :first="d_first" :totalRecords="getTotalRecords" :pageLinkSize="pageLinkSize" :template="paginatorTemplate" :rowsPerPageOptions="rowsPerPageOptions" <DVPaginator
:currentPageReportTemplate="currentPageReportTemplate" :class="{'p-paginator-bottom': paginatorBottom}" :alwaysShow="alwaysShowPaginator" @page="onPage($event)"> v-if="paginatorBottom"
<template #start v-if="$slots.paginatorstart"> :rows="d_rows"
:first="d_first"
:totalRecords="getTotalRecords"
:pageLinkSize="pageLinkSize"
:template="paginatorTemplate"
:rowsPerPageOptions="rowsPerPageOptions"
:currentPageReportTemplate="currentPageReportTemplate"
:class="{ 'p-paginator-bottom': paginatorBottom }"
:alwaysShow="alwaysShowPaginator"
@page="onPage($event)"
>
<template v-if="$slots.paginatorstart" #start>
<slot name="paginatorstart"></slot> <slot name="paginatorstart"></slot>
</template> </template>
<template #end v-if="$slots.paginatorend"> <template v-if="$slots.paginatorend" #end>
<slot name="paginatorend"></slot> <slot name="paginatorend"></slot>
</template> </template>
</DVPaginator> </DVPaginator>
<div class="p-dataview-footer" v-if="$slots.footer"> <div v-if="$slots.footer" class="p-dataview-footer">
<slot name="footer"></slot> <slot name="footer"></slot>
</div> </div>
</div> </div>
@ -116,7 +138,7 @@ export default {
return { return {
d_first: this.first, d_first: this.first,
d_rows: this.rows d_rows: this.rows
} };
}, },
watch: { watch: {
first(newValue) { first(newValue) {
@ -153,23 +175,17 @@ export default {
let value2 = ObjectUtils.resolveFieldData(data2, this.sortField); let value2 = ObjectUtils.resolveFieldData(data2, this.sortField);
let result = null; let result = null;
if (value1 == null && value2 != null) if (value1 == null && value2 != null) result = -1;
result = -1; else if (value1 != null && value2 == null) result = 1;
else if (value1 != null && value2 == null) else if (value1 == null && value2 == null) result = 0;
result = 1; else if (typeof value1 === 'string' && typeof value2 === 'string') result = value1.localeCompare(value2, undefined, { numeric: true });
else if (value1 == null && value2 == null) else result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0;
result = 0;
else if (typeof value1 === 'string' && typeof value2 === 'string')
result = value1.localeCompare(value2, undefined, { numeric: true });
else
result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
return (this.sortOrder * result); return this.sortOrder * result;
}); });
return value; return value;
} } else {
else {
return null; return null;
} }
}, },
@ -180,20 +196,20 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-dataview p-component', { return [
'p-dataview-list': (this.layout === 'list'), 'p-dataview p-component',
'p-dataview-grid': (this.layout === 'grid') {
'p-dataview-list': this.layout === 'list',
'p-dataview-grid': this.layout === 'grid'
} }
] ];
}, },
getTotalRecords() { getTotalRecords() {
if (this.totalRecords) if (this.totalRecords) return this.totalRecords;
return this.totalRecords; else return this.value ? this.value.length : 0;
else
return this.value ? this.value.length : 0;
}, },
empty() { empty() {
return (!this.value || this.value.length === 0); return !this.value || this.value.length === 0;
}, },
paginatorTop() { paginatorTop() {
return this.paginator && (this.paginatorPosition !== 'bottom' || this.paginatorPosition === 'both'); return this.paginator && (this.paginatorPosition !== 'bottom' || this.paginatorPosition === 'both');
@ -211,20 +227,18 @@ export default {
if (this.paginator) { if (this.paginator) {
const first = this.lazy ? 0 : this.d_first; const first = this.lazy ? 0 : this.d_first;
return data.slice(first, first + this.d_rows); return data.slice(first, first + this.d_rows);
} } else {
else {
return data; return data;
} }
} else {
}
else {
return null; return null;
} }
} }
}, },
components: { components: {
'DVPaginator': Paginator DVPaginator: Paginator
}
} }
};
</script> </script>

View file

@ -7,8 +7,7 @@ export interface DataViewLayoutOptionsProps {
modelValue?: string | undefined; modelValue?: string | undefined;
} }
export interface DataViewLayoutOptionsSlots { export interface DataViewLayoutOptionsSlots {}
}
export declare type DataViewLayoutOptionsEmits = { export declare type DataViewLayoutOptionsEmits = {
/** /**
@ -16,13 +15,13 @@ export declare type DataViewLayoutOptionsEmits = {
* @param {*} value - New value. * @param {*} value - New value.
*/ */
'update:modelValue': (value: string) => void; 'update:modelValue': (value: string) => void;
} };
declare class DataViewLayoutOptions extends ClassComponent<DataViewLayoutOptionsProps, DataViewLayoutOptionsSlots, DataViewLayoutOptionsEmits> {} declare class DataViewLayoutOptions extends ClassComponent<DataViewLayoutOptionsProps, DataViewLayoutOptionsSlots, DataViewLayoutOptionsEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
DataViewLayoutOptions: GlobalComponentConstructor<DataViewLayoutOptions> DataViewLayoutOptions: GlobalComponentConstructor<DataViewLayoutOptions>;
} }
} }
@ -33,7 +32,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [DataViewLayoutOptions](https://www.primefaces.org/primevue/showcase/#/dataview) * - [DataViewLayoutOptions](https://www.primefaces.org/primevue/dataview)
* *
*/ */
export default DataViewLayoutOptions; export default DataViewLayoutOptions;

View file

@ -16,24 +16,18 @@
props: { props: {
modelValue: String modelValue: String
}, },
computed: {
buttonListClass(){
return [
'p-button p-button-icon-only',
{'p-highlight': this.modelValue === 'list'}
]
},
buttonGridClass() {
return [
'p-button p-button-icon-only',
{'p-highlight': this.modelValue === 'grid'}
]
}
},
methods: { methods: {
changeLayout(layout) { changeLayout(layout) {
this.$emit('update:modelValue', layout); this.$emit('update:modelValue', layout);
} }
},
computed: {
buttonListClass() {
return ['p-button p-button-icon-only', { 'p-highlight': this.modelValue === 'list' }];
},
buttonGridClass() {
return ['p-button p-button-icon-only', { 'p-highlight': this.modelValue === 'grid' }];
} }
} }
};
</script> </script>

View file

@ -1,8 +1,7 @@
import { VNode } from 'vue'; import { VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
export interface DeferredContentProps { export interface DeferredContentProps {}
}
export interface DeferredContentSlots { export interface DeferredContentSlots {
/** /**
@ -15,14 +14,14 @@ export declare type DeferredContentEmits = {
/** /**
* Callback to invoke when deferred content is loaded. * Callback to invoke when deferred content is loaded.
*/ */
'load': () => void; load: () => void;
} };
declare class DeferredContent extends ClassComponent<DeferredContentProps, DeferredContentSlots, DeferredContentEmits> {} declare class DeferredContent extends ClassComponent<DeferredContentProps, DeferredContentSlots, DeferredContentEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
DeferredContent: GlobalComponentConstructor<DeferredContent> DeferredContent: GlobalComponentConstructor<DeferredContent>;
} }
} }
@ -32,7 +31,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [DeferredContent](https://www.primefaces.org/primevue/showcase/#/deferredcontent) * - [DeferredContent](https://www.primefaces.org/primevue/deferredcontent)
* *
*/ */
export default DeferredContent; export default DeferredContent;

View file

@ -11,14 +11,12 @@ export default {
data() { data() {
return { return {
loaded: false loaded: false
} };
}, },
mounted() { mounted() {
if (!this.loaded) { if (!this.loaded) {
if (this.shouldLoad()) if (this.shouldLoad()) this.load();
this.load(); else this.bindScrollListener();
else
this.bindScrollListener();
} }
}, },
beforeUnmount() { beforeUnmount() {
@ -44,13 +42,12 @@ export default {
shouldLoad() { shouldLoad() {
if (this.loaded) { if (this.loaded) {
return false; return false;
} } else {
else {
const rect = this.$refs.container.getBoundingClientRect(); const rect = this.$refs.container.getBoundingClientRect();
const docElement = document.documentElement; const docElement = document.documentElement;
const winHeight = docElement.clientHeight; const winHeight = docElement.clientHeight;
return (winHeight >= rect.top); return winHeight >= rect.top;
} }
}, },
load(event) { load(event) {
@ -58,5 +55,5 @@ export default {
this.$emit('load', event); this.$emit('load', event);
} }
} }
} };
</script> </script>

View file

@ -158,7 +158,7 @@ export declare type DialogEmits = {
/** /**
* Callback to invoke when dialog is hidden. * Callback to invoke when dialog is hidden.
*/ */
'hide': () => void; hide: () => void;
/** /**
* Callback to invoke after dialog is hidden. * Callback to invoke after dialog is hidden.
*/ */
@ -166,29 +166,29 @@ export declare type DialogEmits = {
/** /**
* Callback to invoke when dialog is shown. * Callback to invoke when dialog is shown.
*/ */
'show': () => void; show: () => void;
/** /**
* Fired when a dialog gets maximized. * Fired when a dialog gets maximized.
* @param {event} event - Browser event. * @param {event} event - Browser event.
*/ */
'maximize': (event: Event) => void; maximize: (event: Event) => void;
/** /**
* Fired when a dialog gets unmaximized. * Fired when a dialog gets unmaximized.
* @param {event} event - Browser event. * @param {event} event - Browser event.
*/ */
'unmaximize': (event: Event) => void; unmaximize: (event: Event) => void;
/** /**
* Fired when a dialog drag completes. * Fired when a dialog drag completes.
* @param {event} event - Browser event. * @param {event} event - Browser event.
*/ */
'dragend': (event: Event) => void; dragend: (event: Event) => void;
} };
declare class Dialog extends ClassComponent<DialogProps, DialogSlots, DialogEmits> {} declare class Dialog extends ClassComponent<DialogProps, DialogSlots, DialogEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Dialog: GlobalComponentConstructor<Dialog> Dialog: GlobalComponentConstructor<Dialog>;
} }
} }
@ -198,7 +198,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Dialog](https://www.primefaces.org/primevue/showcase/#/dialog) * - [Dialog](https://www.primefaces.org/primevue/dialog)
* *
*/ */
export default Dialog; export default Dialog;

View file

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
import PrimeVue from '@/components/config/PrimeVue'; import PrimeVue from '@/components/config/PrimeVue';
import { mount } from '@vue/test-utils';
import Dialog from './Dialog.vue'; import Dialog from './Dialog.vue';
describe('Dialog.vue', () => { describe('Dialog.vue', () => {
@ -40,7 +40,7 @@ describe('Dialog.vue', () => {
} }
}); });
expect(wrapper.find('.p-dialog-content').exists()).toBe(true); expect(wrapper.find('.p-dialog-content').exists()).toBe(false);
expect(wrapper.find('.p-dialog-footer').exists()).toBe(true); expect(wrapper.find('.p-dialog-footer').exists()).toBe(false);
}); });
}); });

View file

@ -1,17 +1,17 @@
<template> <template>
<Portal :appendTo="appendTo"> <Portal :appendTo="appendTo">
<div :ref="maskRef" :class="maskClass" v-if="containerVisible" @click="onMaskClick"> <div v-if="containerVisible" :ref="maskRef" :class="maskClass" @click="onMaskClick">
<transition name="p-dialog" @before-enter="onBeforeEnter" @enter="onEnter" @before-leave="onBeforeLeave" @leave="onLeave" @after-leave="onAfterLeave" appear> <transition name="p-dialog" @before-enter="onBeforeEnter" @enter="onEnter" @before-leave="onBeforeLeave" @leave="onLeave" @after-leave="onAfterLeave" appear>
<div :ref="containerRef" :class="dialogClass" v-if="visible" v-bind="$attrs" role="dialog" :aria-labelledby="ariaLabelledById" :aria-modal="modal"> <div v-if="visible" :ref="containerRef" :class="dialogClass" v-bind="$attrs" role="dialog" :aria-labelledby="ariaLabelledById" :aria-modal="modal">
<div class="p-dialog-header" v-if="showHeader" @mousedown="initDrag"> <div v-if="showHeader" class="p-dialog-header" @mousedown="initDrag">
<slot name="header"> <slot name="header">
<span :id="ariaLabelledById" class="p-dialog-title" v-if="header">{{header}}</span> <span v-if="header" :id="ariaLabelledById" class="p-dialog-title">{{ header }}</span>
</slot> </slot>
<div class="p-dialog-header-icons"> <div class="p-dialog-header-icons">
<button class="p-dialog-header-icon p-dialog-header-maximize p-link" @click="maximize" v-if="maximizable" type="button" tabindex="-1" v-ripple> <button v-if="maximizable" v-ripple class="p-dialog-header-icon p-dialog-header-maximize p-link" @click="maximize" type="button" tabindex="-1">
<span :class="maximizeIconClass"></span> <span :class="maximizeIconClass"></span>
</button> </button>
<button class="p-dialog-header-icon p-dialog-header-close p-link" @click="close" v-if="closable" :aria-label="ariaCloseLabel" type="button" v-ripple> <button v-if="closable" v-ripple class="p-dialog-header-icon p-dialog-header-close p-link" @click="close" :aria-label="ariaCloseLabel" type="button">
<span class="p-dialog-header-close-icon pi pi-times"></span> <span class="p-dialog-header-close-icon pi pi-times"></span>
</button> </button>
</div> </div>
@ -19,7 +19,7 @@
<div :class="contentStyleClass" :style="contentStyle"> <div :class="contentStyleClass" :style="contentStyle">
<slot></slot> <slot></slot>
</div> </div>
<div class="p-dialog-footer" v-if="footer || $slots.footer"> <div v-if="footer || $slots.footer" class="p-dialog-footer">
<slot name="footer">{{ footer }}</slot> <slot name="footer">{{ footer }}</slot>
</div> </div>
</div> </div>
@ -105,13 +105,13 @@ export default {
provide() { provide() {
return { return {
dialogRef: computed(() => this._instance) dialogRef: computed(() => this._instance)
} };
}, },
data() { data() {
return { return {
containerVisible: this.visible, containerVisible: this.visible,
maximized: false maximized: false
} };
}, },
documentKeydownListener: null, documentKeydownListener: null,
container: null, container: null,
@ -135,6 +135,7 @@ export default {
if (this.mask && this.autoZIndex) { if (this.mask && this.autoZIndex) {
ZIndexUtils.clear(this.mask); ZIndexUtils.clear(this.mask);
} }
this.container = null; this.container = null;
this.mask = null; this.mask = null;
}, },
@ -172,6 +173,7 @@ export default {
if (this.autoZIndex) { if (this.autoZIndex) {
ZIndexUtils.clear(this.mask); ZIndexUtils.clear(this.mask);
} }
this.containerVisible = false; this.containerVisible = false;
this.unbindDocumentState(); this.unbindDocumentState();
this.unbindGlobalListeners(); this.unbindGlobalListeners();
@ -184,6 +186,7 @@ export default {
}, },
focus() { focus() {
let focusTarget = this.container.querySelector('[autofocus]'); let focusTarget = this.container.querySelector('[autofocus]');
if (focusTarget) { if (focusTarget) {
focusTarget.focus(); focusTarget.focus();
} }
@ -192,17 +195,14 @@ export default {
if (this.maximized) { if (this.maximized) {
this.maximized = false; this.maximized = false;
this.$emit('unmaximize', event); this.$emit('unmaximize', event);
} } else {
else {
this.maximized = true; this.maximized = true;
this.$emit('maximize', event); this.$emit('maximize', event);
} }
if (!this.modal) { if (!this.modal) {
if (this.maximized) if (this.maximized) DomHandler.addClass(document.body, 'p-overflow-hidden');
DomHandler.addClass(document.body, 'p-overflow-hidden'); else DomHandler.removeClass(document.body, 'p-overflow-hidden');
else
DomHandler.removeClass(document.body, 'p-overflow-hidden');
} }
}, },
enableDocumentSettings() { enableDocumentSettings() {
@ -219,23 +219,19 @@ export default {
if (event.which === 9) { if (event.which === 9) {
event.preventDefault(); event.preventDefault();
let focusableElements = DomHandler.getFocusableElements(this.container); let focusableElements = DomHandler.getFocusableElements(this.container);
if (focusableElements && focusableElements.length > 0) { if (focusableElements && focusableElements.length > 0) {
if (!document.activeElement) { if (!document.activeElement) {
focusableElements[0].focus(); focusableElements[0].focus();
} } else {
else {
let focusedIndex = focusableElements.indexOf(document.activeElement); let focusedIndex = focusableElements.indexOf(document.activeElement);
if (event.shiftKey) { if (event.shiftKey) {
if (focusedIndex == -1 || focusedIndex === 0) if (focusedIndex == -1 || focusedIndex === 0) focusableElements[focusableElements.length - 1].focus();
focusableElements[focusableElements.length - 1].focus(); else focusableElements[focusedIndex - 1].focus();
else } else {
focusableElements[focusedIndex - 1].focus(); if (focusedIndex == -1 || focusedIndex === focusableElements.length - 1) focusableElements[0].focus();
} else focusableElements[focusedIndex + 1].focus();
else {
if (focusedIndex == -1 || focusedIndex === (focusableElements.length - 1))
focusableElements[0].focus();
else
focusableElements[focusedIndex + 1].focus();
} }
} }
} }
@ -257,7 +253,7 @@ export default {
}, },
getPositionClass() { getPositionClass() {
const positions = ['left', 'right', 'top', 'topleft', 'topright', 'bottom', 'bottomleft', 'bottomright']; const positions = ['left', 'right', 'top', 'topleft', 'topright', 'bottom', 'bottomleft', 'bottomright'];
const pos = positions.find(item => item === this.position); const pos = positions.find((item) => item === this.position);
return pos ? `p-dialog-${pos}` : ''; return pos ? `p-dialog-${pos}` : '';
}, },
@ -274,6 +270,7 @@ export default {
document.head.appendChild(this.styleElement); document.head.appendChild(this.styleElement);
let innerHTML = ''; let innerHTML = '';
for (let breakpoint in this.breakpoints) { for (let breakpoint in this.breakpoints) {
innerHTML += ` innerHTML += `
@media screen and (max-width: ${breakpoint}) { @media screen and (max-width: ${breakpoint}) {
@ -281,7 +278,7 @@ export default {
width: ${this.breakpoints[breakpoint]} !important; width: ${this.breakpoints[breakpoint]} !important;
} }
} }
` `;
} }
this.styleElement.innerHTML = innerHTML; this.styleElement.innerHTML = innerHTML;
@ -337,24 +334,24 @@ export default {
this.container.style.position = 'fixed'; this.container.style.position = 'fixed';
if (this.keepInViewport) { if (this.keepInViewport) {
if (leftPos >= this.minX && (leftPos + width) < viewport.width) { if (leftPos >= this.minX && leftPos + width < viewport.width) {
this.lastPageX = event.pageX; this.lastPageX = event.pageX;
this.container.style.left = leftPos + 'px'; this.container.style.left = leftPos + 'px';
} }
if (topPos >= this.minY && (topPos + height) < viewport.height) { if (topPos >= this.minY && topPos + height < viewport.height) {
this.lastPageY = event.pageY; this.lastPageY = event.pageY;
this.container.style.top = topPos + 'px'; this.container.style.top = topPos + 'px';
} }
} } else {
else {
this.lastPageX = event.pageX; this.lastPageX = event.pageX;
this.container.style.left = leftPos + 'px'; this.container.style.left = leftPos + 'px';
this.lastPageY = event.pageY; this.lastPageY = event.pageY;
this.container.style.top = topPos + 'px'; this.container.style.top = topPos + 'px';
} }
} }
} };
window.document.addEventListener('mousemove', this.documentDragListener); window.document.addEventListener('mousemove', this.documentDragListener);
}, },
unbindDocumentDragListener() { unbindDocumentDragListener() {
@ -372,6 +369,7 @@ export default {
this.$emit('dragend', event); this.$emit('dragend', event);
} }
}; };
window.document.addEventListener('mouseup', this.documentDragEndListener); window.document.addEventListener('mouseup', this.documentDragEndListener);
}, },
unbindDocumentDragEndListener() { unbindDocumentDragEndListener() {
@ -386,18 +384,24 @@ export default {
return ['p-dialog-mask', { 'p-component-overlay p-component-overlay-enter': this.modal }, this.getPositionClass()]; return ['p-dialog-mask', { 'p-component-overlay p-component-overlay-enter': this.modal }, this.getPositionClass()];
}, },
dialogClass() { dialogClass() {
return ['p-dialog p-component', { return [
'p-dialog p-component',
{
'p-dialog-rtl': this.rtl, 'p-dialog-rtl': this.rtl,
'p-dialog-maximized': this.maximizable && this.maximized, 'p-dialog-maximized': this.maximizable && this.maximized,
'p-input-filled': this.$primevue.config.inputStyle === 'filled', 'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false 'p-ripple-disabled': this.$primevue.config.ripple === false
}]; }
];
}, },
maximizeIconClass() { maximizeIconClass() {
return ['p-dialog-header-maximize-icon pi', { return [
'p-dialog-header-maximize-icon pi',
{
'pi-window-maximize': !this.maximized, 'pi-window-maximize': !this.maximized,
'pi-window-minimize': this.maximized 'pi-window-minimize': this.maximized
}]; }
];
}, },
ariaId() { ariaId() {
return UniqueComponentId(); return UniqueComponentId();
@ -413,12 +417,12 @@ export default {
} }
}, },
directives: { directives: {
'ripple': Ripple ripple: Ripple
}, },
components: { components: {
'Portal': Portal Portal: Portal
}
} }
};
</script> </script>
<style> <style>
.p-dialog-mask { .p-dialog-mask {
@ -484,7 +488,7 @@ export default {
transition: all 150ms cubic-bezier(0, 0, 0.2, 1); transition: all 150ms cubic-bezier(0, 0, 0.2, 1);
} }
.p-dialog-leave-active { .p-dialog-leave-active {
transition: all 150ms cubic-bezier(0.4, 0.0, 0.2, 1); transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1);
} }
.p-dialog-enter-from, .p-dialog-enter-from,
.p-dialog-leave-to { .p-dialog-leave-to {
@ -501,7 +505,7 @@ export default {
.p-dialog-topright .p-dialog, .p-dialog-topright .p-dialog,
.p-dialog-bottomleft .p-dialog, .p-dialog-bottomleft .p-dialog,
.p-dialog-bottomright .p-dialog { .p-dialog-bottomright .p-dialog {
margin: .75rem; margin: 0.75rem;
transform: translate3d(0px, 0px, 0px); transform: translate3d(0px, 0px, 0px);
} }
.p-dialog-top .p-dialog-enter-active, .p-dialog-top .p-dialog-enter-active,
@ -520,7 +524,7 @@ export default {
.p-dialog-bottomleft .p-dialog-leave-active, .p-dialog-bottomleft .p-dialog-leave-active,
.p-dialog-bottomright .p-dialog-enter-active, .p-dialog-bottomright .p-dialog-enter-active,
.p-dialog-bottomright .p-dialog-leave-active { .p-dialog-bottomright .p-dialog-leave-active {
transition: all .3s ease-out; transition: all 0.3s ease-out;
} }
.p-dialog-top .p-dialog-enter-from, .p-dialog-top .p-dialog-enter-from,
.p-dialog-top .p-dialog-leave-to { .p-dialog-top .p-dialog-leave-to {

View file

@ -13,7 +13,7 @@ export default {
close: (params) => { close: (params) => {
DynamicDialogEventBus.emit('close', { instance, params }); DynamicDialogEventBus.emit('close', { instance, params });
} }
} };
DynamicDialogEventBus.emit('open', { instance }); DynamicDialogEventBus.emit('open', { instance });
@ -25,4 +25,4 @@ export default {
app.config.globalProperties.$dialog = DialogService; app.config.globalProperties.$dialog = DialogService;
app.provide(PrimeVueDialogSymbol, DialogService); app.provide(PrimeVueDialogSymbol, DialogService);
} }
} };

View file

@ -38,14 +38,13 @@ export interface DividerSlots {
default: () => VNode[]; default: () => VNode[];
} }
export declare type DividerEmits = { export declare type DividerEmits = {};
}
declare class Divider extends ClassComponent<DividerProps, DividerSlots, DividerEmits> {} declare class Divider extends ClassComponent<DividerProps, DividerSlots, DividerEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Divider: GlobalComponentConstructor<Divider> Divider: GlobalComponentConstructor<Divider>;
} }
} }
@ -55,7 +54,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Divider](https://www.primefaces.org/primevue/showcase/#/divider) * - [Divider](https://www.primefaces.org/primevue/divider)
* *
*/ */
export default Divider; export default Divider;

View file

@ -1,6 +1,6 @@
<template> <template>
<div :class="containerClass" role="separator"> <div :class="containerClass" role="separator" :aria-orientation="layout">
<div class="p-divider-content" v-if="$slots.default"> <div v-if="$slots.default" class="p-divider-content">
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
@ -25,17 +25,20 @@ export default {
}, },
computed: { computed: {
containerClass() { containerClass() {
return ['p-divider p-component', 'p-divider-' + this.layout, 'p-divider-' + this.type, return [
'p-divider p-component',
'p-divider-' + this.layout,
'p-divider-' + this.type,
{ 'p-divider-left': this.layout === 'horizontal' && (!this.align || this.align === 'left') }, { 'p-divider-left': this.layout === 'horizontal' && (!this.align || this.align === 'left') },
{ 'p-divider-center': this.layout === 'horizontal' && this.align === 'center' }, { 'p-divider-center': this.layout === 'horizontal' && this.align === 'center' },
{ 'p-divider-right': this.layout === 'horizontal' && this.align === 'right' }, { 'p-divider-right': this.layout === 'horizontal' && this.align === 'right' },
{'p-divider-top': this.layout === 'vertical' && (this.align === 'top')}, { 'p-divider-top': this.layout === 'vertical' && this.align === 'top' },
{ 'p-divider-center': this.layout === 'vertical' && (!this.align || this.align === 'center') }, { 'p-divider-center': this.layout === 'vertical' && (!this.align || this.align === 'center') },
{ 'p-divider-bottom': this.layout === 'vertical' && this.align === 'bottom' } { 'p-divider-bottom': this.layout === 'vertical' && this.align === 'bottom' }
]; ];
} }
} }
} };
</script> </script>
<style> <style>
@ -52,7 +55,7 @@ export default {
top: 50%; top: 50%;
left: 0; left: 0;
width: 100%; width: 100%;
content: ""; content: '';
} }
.p-divider-horizontal.p-divider-left { .p-divider-horizontal.p-divider-left {
@ -85,7 +88,7 @@ export default {
top: 0; top: 0;
left: 50%; left: 50%;
height: 100%; height: 100%;
content: ""; content: '';
} }
.p-divider-vertical.p-divider-top { .p-divider-vertical.p-divider-top {

View file

@ -78,14 +78,13 @@ export interface DockSlots {
}) => VNode[]; }) => VNode[];
} }
export declare type DockEmits = { export declare type DockEmits = {};
}
declare class Dock extends ClassComponent<DockProps, DockSlots, DockEmits> {} declare class Dock extends ClassComponent<DockProps, DockSlots, DockEmits> {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Dock: GlobalComponentConstructor<Dock> Dock: GlobalComponentConstructor<Dock>;
} }
} }
@ -95,11 +94,11 @@ declare module '@vue/runtime-core' {
* *
* Helper API: * Helper API:
* *
* - [MenuItem](https://www.primefaces.org/primevue/showcase/#/menumodel) * - [MenuItem](https://www.primefaces.org/primevue/menumodel)
* *
* Demos: * Demos:
* *
* - [Dock](https://www.primefaces.org/primevue/showcase/#/dock) * - [Dock](https://www.primefaces.org/primevue/dock)
* *
*/ */
export default Dock; export default Dock;

View file

@ -12,7 +12,7 @@ export default {
props: { props: {
position: { position: {
type: String, type: String,
default: "bottom" default: 'bottom'
}, },
model: null, model: null,
class: null, class: null,
@ -31,7 +31,7 @@ export default {
components: { components: {
DockSub DockSub
} }
} };
</script> </script>
<style> <style>
@ -59,7 +59,7 @@ export default {
} }
.p-dock-item { .p-dock-item {
transition: all .2s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform; will-change: transform;
} }

View file

@ -1,21 +1,35 @@
<template> <template>
<div class="p-dock-list-container"> <div class="p-dock-list-container">
<ul ref="list" class="p-dock-list" role="menu" @mouseleave="onListMouseLeave"> <ul ref="list" class="p-dock-list" role="menu" @mouseleave="onListMouseLeave">
<li v-for="(item, index) of model" :class="itemClass(index)" :key="index" role="none" @mouseenter="onItemMouseEnter(index)"> <li v-for="(item, index) of model" :key="index" :class="itemClass(index)" role="none" @mouseenter="onItemMouseEnter(index)">
<template v-if="!templates['item']"> <template v-if="!templates['item']">
<router-link v-if="item.to && !disabled(item)" :to="item.to" custom v-slot="{navigate, href, isActive, isExactActive}"> <router-link v-if="item.to && !disabled(item)" v-slot="{ navigate, href, isActive, isExactActive }" :to="item.to" custom>
<a :href="href" role="menuitem" :class="linkClass(item, {isActive, isExactActive})" :target="item.target" <a
v-tooltip:[tooltipOptions]="{value: item.label, disabled: !tooltipOptions}" @click="onItemClick($event, item, navigate)"> v-tooltip:[tooltipOptions]="{ value: item.label, disabled: !tooltipOptions }"
:href="href"
role="menuitem"
:class="linkClass(item, { isActive, isExactActive })"
:target="item.target"
@click="onItemClick($event, item, navigate)"
>
<template v-if="!templates['icon']"> <template v-if="!templates['icon']">
<span :class="['p-dock-action-icon', item.icon]" v-ripple></span> <span v-ripple :class="['p-dock-action-icon', item.icon]"></span>
</template> </template>
<component v-else :is="templates['icon']" :item="item"></component> <component v-else :is="templates['icon']" :item="item"></component>
</a> </a>
</router-link> </router-link>
<a v-else :href="item.url" role="menuitem" :class="linkClass(item)" :target="item.target" <a
v-tooltip:[tooltipOptions]="{value: item.label, disabled: !tooltipOptions}" @click="onItemClick($event, item)" :tabindex="disabled(item) ? null : '0'"> v-else
v-tooltip:[tooltipOptions]="{ value: item.label, disabled: !tooltipOptions }"
:href="item.url"
role="menuitem"
:class="linkClass(item)"
:target="item.target"
@click="onItemClick($event, item)"
:tabindex="disabled(item) ? null : '0'"
>
<template v-if="!templates['icon']"> <template v-if="!templates['icon']">
<span :class="['p-dock-action-icon', item.icon]" v-ripple></span> <span v-ripple :class="['p-dock-action-icon', item.icon]"></span>
</template> </template>
<component v-else :is="templates['icon']" :item="item"></component> <component v-else :is="templates['icon']" :item="item"></component>
</a> </a>
@ -50,7 +64,7 @@ export default {
data() { data() {
return { return {
currentIndex: -3 currentIndex: -3
} };
}, },
methods: { methods: {
onListMouseLeave() { onListMouseLeave() {
@ -62,6 +76,7 @@ export default {
onItemClick(event, item, navigate) { onItemClick(event, item, navigate) {
if (this.disabled(item)) { if (this.disabled(item)) {
event.preventDefault(); event.preventDefault();
return; return;
} }
@ -77,28 +92,34 @@ export default {
} }
}, },
itemClass(index) { itemClass(index) {
return ['p-dock-item', { return [
'p-dock-item-second-prev': (this.currentIndex - 2) === index, 'p-dock-item',
'p-dock-item-prev': (this.currentIndex - 1) === index, {
'p-dock-item-second-prev': this.currentIndex - 2 === index,
'p-dock-item-prev': this.currentIndex - 1 === index,
'p-dock-item-current': this.currentIndex === index, 'p-dock-item-current': this.currentIndex === index,
'p-dock-item-next': (this.currentIndex + 1) === index, 'p-dock-item-next': this.currentIndex + 1 === index,
'p-dock-item-second-next': (this.currentIndex + 2) === index 'p-dock-item-second-next': this.currentIndex + 2 === index
}]; }
];
}, },
linkClass(item, routerProps) { linkClass(item, routerProps) {
return ['p-dock-action', { return [
'p-dock-action',
{
'p-disabled': this.disabled(item), 'p-disabled': this.disabled(item),
'router-link-active': routerProps && routerProps.isActive, 'router-link-active': routerProps && routerProps.isActive,
'router-link-active-exact': this.exact && routerProps && routerProps.isExactActive 'router-link-active-exact': this.exact && routerProps && routerProps.isExactActive
}]; }
];
}, },
disabled(item) { disabled(item) {
return (typeof item.disabled === 'function' ? item.disabled() : item.disabled); return typeof item.disabled === 'function' ? item.disabled() : item.disabled;
} }
}, },
directives: { directives: {
'ripple': Ripple, ripple: Ripple,
'tooltip': Tooltip tooltip: Tooltip
}
} }
};
</script> </script>

View file

@ -1,6 +1,6 @@
import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue'; import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers'; import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
import { VirtualScrollerProps, VirtualScrollerItemOptions } from '../virtualscroller'; import { VirtualScrollerItemOptions, VirtualScrollerProps } from '../virtualscroller';
type DropdownOptionLabelType = string | ((data: any) => string) | undefined; type DropdownOptionLabelType = string | ((data: any) => string) | undefined;
@ -168,6 +168,10 @@ export interface DropdownProps {
* Default value is 'pi pi-spinner pi-spin'. * Default value is 'pi pi-spinner pi-spin'.
*/ */
loadingIcon?: string | undefined; loadingIcon?: string | undefined;
/**
* Clears the filter value when hiding the dropdown.
*/
resetFilterOnHide?: boolean;
/** /**
* Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it. * Whether to use the virtualScroller feature. The properties of VirtualScroller component can be used like an object in it.
* @see VirtualScroller.VirtualScrollerProps * @see VirtualScroller.VirtualScrollerProps
@ -178,6 +182,16 @@ export interface DropdownProps {
* Default value is true. * Default value is true.
*/ */
autoOptionFocus?: boolean | undefined; autoOptionFocus?: boolean | undefined;
/**
* Whether to focus on the filter element when the overlay panel is shown.
* Default value is false.
*/
autoFilterFocus?: boolean | undefined;
/**
* When enabled, the focused option is selected.
* Default value is false.
*/
selectOnFocus?: boolean | undefined;
/** /**
* Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration. * Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
* Default value is '{0} results are available'. * Default value is '{0} results are available'.
@ -210,11 +224,11 @@ export interface DropdownProps {
/** /**
* Defines a string value that labels an interactive element. * Defines a string value that labels an interactive element.
*/ */
"aria-label"?: string | undefined; 'aria-label'?: string | undefined;
/** /**
* Identifier of the underlying input element. * Identifier of the underlying input element.
*/ */
"aria-labelledby"?: string | undefined; 'aria-labelledby'?: string | undefined;
} }
export interface DropdownSlots { export interface DropdownSlots {
@ -347,17 +361,17 @@ export declare type DropdownEmits = {
* Callback to invoke on value change. * Callback to invoke on value change.
* @param {DropdownChangeEvent} event - Custom change event. * @param {DropdownChangeEvent} event - Custom change event.
*/ */
'change': (event: DropdownChangeEvent) => void; change: (event: DropdownChangeEvent) => void;
/** /**
* Callback to invoke when the component receives focus. * Callback to invoke when the component receives focus.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'focus': (event: Event) => void; focus: (event: Event) => void;
/** /**
* Callback to invoke when the component loses focus. * Callback to invoke when the component loses focus.
* @param {Event} event - Browser event. * @param {Event} event - Browser event.
*/ */
'blur': (event: Event) => void; blur: (event: Event) => void;
/** /**
* Callback to invoke before the overlay is shown. * Callback to invoke before the overlay is shown.
*/ */
@ -369,17 +383,17 @@ export declare type DropdownEmits = {
/** /**
* Callback to invoke when the overlay is shown. * Callback to invoke when the overlay is shown.
*/ */
'show': () => void; show: () => void;
/** /**
* Callback to invoke when the overlay is hidden. * Callback to invoke when the overlay is hidden.
*/ */
'hide': () => void; hide: () => void;
/** /**
* Callback to invoke on filter input. * Callback to invoke on filter input.
* @param {DropdownFilterEvent} event - Custom filter event. * @param {DropdownFilterEvent} event - Custom filter event.
*/ */
'filter': (event: DropdownFilterEvent) => void; filter: (event: DropdownFilterEvent) => void;
} };
declare class Dropdown extends ClassComponent<DropdownProps, DropdownSlots, DropdownEmits> { declare class Dropdown extends ClassComponent<DropdownProps, DropdownSlots, DropdownEmits> {
/** /**
@ -400,7 +414,7 @@ declare class Dropdown extends ClassComponent<DropdownProps, DropdownSlots, Drop
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface GlobalComponents { interface GlobalComponents {
Dropdown: GlobalComponentConstructor<Dropdown> Dropdown: GlobalComponentConstructor<Dropdown>;
} }
} }
@ -410,7 +424,7 @@ declare module '@vue/runtime-core' {
* *
* Demos: * Demos:
* *
* - [Dropdown](https://www.primefaces.org/primevue/showcase/#/dropdown) * - [Dropdown](https://www.primefaces.org/primevue/dropdown)
* *
*/ */
export default Dropdown; export default Dropdown;

Some files were not shown because too many files have changed in this diff Show more