Merge branch 'master' into issue-3166

pull/3463/head
Bahadır 2023-01-09 21:32:42 +03:00 committed by GitHub
commit 13b9fd9537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 874 additions and 306 deletions

View File

@ -387,11 +387,15 @@ const AutoCompleteSlots = [
},
{
name: 'content',
description: 'Custom content for the virtual scroller'
description: 'Custom content for the virtual scroller.'
},
{
name: 'loader',
description: 'Custom content for the virtual scroller loader items'
description: 'Custom content for the virtual scroller loader items.'
},
{
name: 'empty',
description: 'Custom empty template when there is no data to display.'
}
];

View File

@ -46,6 +46,12 @@ const SidebarProps = [
type: 'string',
default: 'close',
description: 'Aria label of the close icon.'
},
{
name: 'blockScroll',
type: 'boolean',
default: 'false',
description: 'Whether background scroll should be blocked when sidebar is visible.'
}
];

View File

@ -0,0 +1,298 @@
import { afterEach } from 'vitest';
import FilterService from '../FilterService';
const filters = FilterService.filters;
import { ObjectUtils } from 'primevue/utils';
afterEach(() => {
vi.restoreAllMocks();
});
const checkParametersNullOrUndefined = (filterType) => {
it('When value parameter is undefined', () => {
expect(filters[filterType]('value', undefined)).toBeTruthy();
});
it('When value parameter is null', () => {
expect(filters[filterType]('value', null)).toBeTruthy();
});
it('When filter parameter is undefined', () => {
expect(filters[filterType](undefined, 'filter')).toBeFalsy();
});
it('When filter parameter is null', () => {
expect(filters[filterType](undefined, 'filter')).toBeFalsy();
});
};
describe('FilterService', () => {
describe('StartsWith filter test', () => {
checkParametersNullOrUndefined('startsWith');
it('When value and filter parameter is not null or undefined', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockReturnValue('value');
const startsWith = filters.startsWith('value', 'filter', 'tr');
expect(startsWith).toBeTruthy();
});
});
describe('Contains filter test', () => {
checkParametersNullOrUndefined('contains');
it('When value and filter parameter is not null or undefined', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockReturnValue('value');
const contains = filters.contains('value', 'filter', 'tr');
expect(contains).toBeTruthy();
});
});
describe('NotContains filter test', () => {
checkParametersNullOrUndefined('notContains');
it('When value and filter parameter is not null or undefined', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockReturnValue('value');
const notContains = filters.notContains('value', 'filter', 'tr');
expect(notContains).toBeFalsy();
});
});
describe('endsWith filter test', () => {
checkParametersNullOrUndefined('endsWith');
it('When value and filter parameter is not null or undefined', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockReturnValue('value');
const endsWith = filters.endsWith('value', 'filter', 'tr');
expect(endsWith).toBeTruthy();
});
});
describe('equals filter test', () => {
checkParametersNullOrUndefined('equals');
it('When value and filter parameter has getTime property', () => {
const getTimeMock = vi.fn(() => true);
const equals = filters.equals({ getTime: getTimeMock }, { getTime: getTimeMock }, 'tr');
expect(equals).toBeTruthy();
});
it('When value and filter parameter is not null or undefined', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockReturnValue('value');
const equals = filters.equals('value', 'filter', 'tr');
expect(equals).toBeTruthy();
});
});
describe('notEquals filter test', () => {
it('When filter parameter is undefined', () => {
expect(filters.notEquals('value', undefined)).toBeFalsy();
});
it('When value parameter is undefined', () => {
expect(filters.notEquals(undefined, 'filter')).toBeTruthy();
});
it('When value parameter is null', () => {
expect(filters.notEquals(undefined, 'filter')).toBeTruthy();
});
it('When value and filter parameter has getTime property', () => {
const getTimeMock = vi.fn(() => true);
const notEquals = filters.notEquals({ getTime: getTimeMock }, { getTime: getTimeMock }, 'tr');
expect(notEquals).toBeFalsy();
});
it('When value and filter parameter is not null or undefined', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockReturnValue('value');
const notEquals = filters.notEquals('value', 'filter', 'tr');
expect(notEquals).toBeFalsy();
});
});
describe('in filter test', () => {
checkParametersNullOrUndefined('in');
it('When value parameter equal to any filter word', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockImplementation((value, filter) => value === filter);
const inFilter = filters.in('e', 'filter');
expect(inFilter).toBeTruthy();
});
it('When value parameter not equal to any filter word', () => {
vi.spyOn(ObjectUtils, 'removeAccents').mockImplementation((value, filter) => value === filter);
const inFilter = filters.in('d', 'filter');
expect(inFilter).toBeFalsy();
});
});
describe('between filter test', () => {
checkParametersNullOrUndefined('between');
it('When value has getTime func and smaller than filter[0]', () => {
const filterGetTime = vi.fn(() => 1);
const filterGetTime1 = vi.fn(() => 3);
const valueGetTime = vi.fn(() => 2);
const between = filters.between({ getTime: valueGetTime }, [{ getTime: filterGetTime }, { getTime: filterGetTime1 }], 'tr');
expect(between).toBeTruthy();
});
it('When value has getTime func and smaller than filter[0]', () => {
const filter = 1;
const filter1 = 2;
const value = 2;
const between = filters.between(value, [filter, filter1], 'tr');
expect(between).toBeTruthy();
});
});
describe('lt filter test', () => {
checkParametersNullOrUndefined('lt');
it('When value has getTime func and smaller than filter', () => {
const filterGetTime = vi.fn(() => 2);
const valueGetTime = vi.fn(() => 1);
const lt = filters.lt({ getTime: valueGetTime }, { getTime: filterGetTime });
expect(lt).toBeTruthy();
});
it('When value smaller than filter', () => {
const filter = 2;
const value = 1;
const lt = filters.lt(value, filter);
expect(lt).toBeTruthy();
});
});
describe('lte filter test', () => {
checkParametersNullOrUndefined('lte');
it('When value has getTime func and smaller than filter', () => {
const filterGetTime = vi.fn(() => 2);
const valueGetTime = vi.fn(() => 1);
const lte = filters.lte({ getTime: valueGetTime }, { getTime: filterGetTime });
expect(lte).toBeTruthy();
});
it('When value smaller than filter', () => {
const filter = 2;
const value = 1;
const lte = filters.lte(value, filter);
expect(lte).toBeTruthy();
});
});
describe('gt filter test', () => {
checkParametersNullOrUndefined('gt');
it('When value has getTime func and smaller than filter[0]', () => {
const filterGetTime = vi.fn(() => 2);
const valueGetTime = vi.fn(() => 1);
const gt = filters.gt({ getTime: valueGetTime }, { getTime: filterGetTime });
expect(gt).toBeFalsy();
});
it('When value smaller than filter[0]', () => {
const filter = 2;
const value = 1;
const gt = filters.gt(value, filter);
expect(gt).toBeFalsy();
});
});
describe('gte filter test', () => {
checkParametersNullOrUndefined('gte');
it('When value has getTime func and smaller than filter[0]', () => {
const filterGetTime = vi.fn(() => 2);
const valueGetTime = vi.fn(() => 1);
const gte = filters.gte({ getTime: valueGetTime }, { getTime: filterGetTime });
expect(gte).toBeFalsy();
});
it('When value parameter smaller than filter[0]', () => {
const filter = 2;
const value = 1;
const gte = filters.gte(value, filter);
expect(gte).toBeFalsy();
});
});
describe('dateIs filter test', () => {
checkParametersNullOrUndefined('dateIs');
it('When value and filter are equal', () => {
const filter = new Date(1993, 6, 28, 14, 39, 7);
const value = new Date(1993, 6, 28, 14, 39, 7);
const dateIs = filters.dateIs(value, filter);
expect(dateIs).toBeTruthy();
});
});
describe('dateIsNot filter test', () => {
checkParametersNullOrUndefined('dateIsNot');
it('When value and filter are not equal', () => {
const filter = new Date(1993, 6, 28, 14, 39, 7);
const value = new Date(1993, 6, 28, 14, 39, 7);
const dateIsNot = filters.dateIsNot(value, filter);
expect(dateIsNot).toBeFalsy();
});
});
describe('dateBefore filter test', () => {
checkParametersNullOrUndefined('dateBefore');
it('When filter value bigger than value', () => {
const filter = new Date(1996, 6, 28, 14, 39, 8);
const value = new Date(1993, 6, 28, 14, 39, 7);
const dateBefore = filters.dateBefore(value, filter);
expect(dateBefore).toBeTruthy();
});
});
describe('dateAfter filter test', () => {
checkParametersNullOrUndefined('dateAfter');
it('When value is not smaller than value', () => {
const filter = new Date(1996, 6, 28, 14, 39, 8);
const value = new Date(1993, 6, 28, 14, 39, 7);
const dateAfter = filters.dateAfter(value, filter);
expect(dateAfter).toBeFalsy();
});
});
});

View File

@ -381,6 +381,10 @@ export interface AutoCompleteSlots {
*/
options: any[];
}) => VNode[];
/**
* Custom empty template when there is no data to display.
*/
empty: () => VNode[];
}
export declare type AutoCompleteEmits = {

View File

@ -119,6 +119,9 @@
<!--TODO: Deprecated since v3.16.0-->
</li>
</template>
<li v-if="!items || (items && items.length === 0)" class="p-autocomplete-empty-message" role="option">
<slot name="empty">{{ searchResultMessageText }}</slot>
</li>
</ul>
</template>
<template v-if="$slots.loader" v-slot:loader="{ options }">
@ -323,7 +326,7 @@ export default {
watch: {
suggestions() {
if (this.searching) {
ObjectUtils.isNotEmpty(this.suggestions) ? this.show() : this.hide();
ObjectUtils.isNotEmpty(this.suggestions) ? this.show() : !!this.$slots.empty ? this.show() : this.hide();
this.focusedOptionIndex = this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
this.searching = false;
}
@ -663,29 +666,19 @@ export default {
this.multiple && event.stopPropagation(); // To prevent onArrowRightKeyOnMultiple method
},
onHomeKey(event) {
const target = event.currentTarget;
const len = target.value.length;
if (event.shiftKey) {
event.currentTarget.setSelectionRange(0, len);
} else {
event.currentTarget.setSelectionRange(0, 0);
}
const { currentTarget } = event;
const len = currentTarget.value.length;
currentTarget.setSelectionRange(0, event.shiftKey ? len : 0);
this.focusedOptionIndex = -1;
event.preventDefault();
},
onEndKey(event) {
const target = event.currentTarget;
const len = target.value.length;
if (event.shiftKey) {
event.currentTarget.setSelectionRange(0, len);
} else {
target.setSelectionRange(len, len);
}
const { currentTarget } = event;
const len = currentTarget.value.length;
currentTarget.setSelectionRange(event.shiftKey ? 0 : len, len);
this.focusedOptionIndex = -1;
event.preventDefault();

View File

@ -1,7 +1,7 @@
<template>
<nav class="p-breadcrumb p-component">
<ol class="p-breadcrumb-list">
<BreadcrumbItem v-if="home" :item="home" class="p-breadcrumb-home" :template="$slots.item" :exact="exact" />
<BreadcrumbItem v-if="home" :item="home" class="p-breadcrumb-home" :exact="exact" />
<template v-for="item of model" :key="item.label">
<li class="p-menuitem-separator">
<span class="pi pi-chevron-right" aria-hidden="true"></span>

View File

@ -0,0 +1,91 @@
import { RouterLinkStub, shallowMount } from '@vue/test-utils';
import { beforeEach, expect } from 'vitest';
import BreadcrumbItem from './BreadcrumbItem.vue';
let wrapper = null;
beforeEach(() => {
wrapper = shallowMount(BreadcrumbItem, {
global: {
mocks: {
$router: {
currentRoute: {
path: vi.fn()
},
navigate: () => true
}
},
stubs: {
'router-link': RouterLinkStub
}
},
props: {
item: { label: 'Computer', visible: () => true },
template: null
}
});
});
afterEach(() => {
vi.clearAllMocks();
});
describe('BreadcrumbItem', () => {
it('When component is mount, text should be exists', () => {
expect(wrapper.find('.p-menuitem-text').exists()).toBe(true);
});
it('When tag is triggered, onClick method should be called', async () => {
const onClickSpy = vi.spyOn(wrapper.vm, 'onClick');
const tag = wrapper.find('a');
tag.trigger('click');
expect(tag.exists()).toBe(true);
expect(onClickSpy).toHaveBeenCalled();
});
it('When onClick method called and item has a command callback, callback should be triggered', async () => {
await wrapper.setProps({ item: { label: 'Computer', visible: () => false, command: vi.fn() } });
const spy = vi.spyOn(wrapper.vm.item, 'command');
wrapper.vm.onClick();
expect(spy).toHaveBeenCalled();
});
it('When linkClass method called and isActive-isExactActive, tag classes should be effected', async () => {
await wrapper.setProps({ exact: true });
expect(wrapper.vm.linkClass({ isActive: true, isExactActive: true })).toEqual([
'p-menuitem-link',
{
'router-link-active': true,
'router-link-active-exact': true
}
]);
});
it('When item prop has a visible, visible method should be return falsy', async () => {
await wrapper.setProps({ item: { label: 'Computer', visible: false, command: vi.fn() } });
expect(wrapper.vm.visible()).toBeFalsy();
});
it('When item prop has a disabled function, disabled method should be return truthy', async () => {
await wrapper.setProps({ item: { disabled: () => true } });
expect(wrapper.vm.disabled()).toBeTruthy();
});
it('When item prop has a label function, disabled method should be return truthy', async () => {
await wrapper.setProps({ item: { label: () => true } });
expect(wrapper.vm.label()).toBeTruthy();
});
it('When item prop has a icon, icon element class should contain item prop icon', async () => {
await wrapper.setProps({ item: { icon: 'pi-discord' } });
expect(wrapper.find('a > span').classes()).toContain('pi-discord');
});
});

View File

@ -1,7 +1,7 @@
import { HTMLAttributes, InputHTMLAttributes, VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
type CalendarValueType = Date | Date[] | undefined;
type CalendarValueType = string | Date | string[] | Date[] | undefined;
type CalendarSlotDateType = { day: number; month: number; year: number; today: boolean; selectable: boolean };

View File

@ -1783,7 +1783,7 @@ export default {
throw 'Invalid Time';
}
this.pm = ampm === this.$primevue.config.locale.am || ampm === this.$primevue.config.locale.am.toLowerCase();
this.pm = ampm === this.$primevue.config.locale.pm || ampm === this.$primevue.config.locale.pm.toLowerCase();
let time = this.parseTime(timeString);
value.setHours(time.hour);

View File

@ -6,8 +6,6 @@
:id="inputId"
type="checkbox"
:value="value"
:class="inputClass"
:style="inputStyle"
:name="name"
:checked="checked"
:tabindex="tabindex"
@ -21,7 +19,7 @@
v-bind="inputProps"
/>
</div>
<div ref="box" :class="['p-checkbox-box', { 'p-highlight': checked, 'p-disabled': disabled, 'p-focus': focused }]">
<div ref="box" :class="['p-checkbox-box', inputClass, { 'p-highlight': checked, 'p-disabled': disabled, 'p-focus': focused }]" :style="inputStyle">
<span :class="['p-checkbox-icon', { 'pi pi-check': checked }]"></span>
</div>
</div>

View File

@ -14,6 +14,10 @@ describe('Chip.vue', () => {
});
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should exists', () => {
expect(wrapper.find('.p-chip.p-component').exists()).toBe(true);
expect(wrapper.find('.p-chip-icon').classes()).toContain('pi-primevue');
@ -26,4 +30,19 @@ describe('Chip.vue', () => {
expect(wrapper.find('.p-chip.p-component').exists()).toBe(false);
});
it('When removable is true and keydown triggered OnKeydown method should be called', async () => {
const closeSpy = vi.spyOn(wrapper.vm, 'onKeydown');
await wrapper.find('.p-chip-remove-icon').trigger('keydown');
expect(closeSpy).toHaveBeenCalled();
});
it('When onKeyDown method triggered close method should be called', async () => {
const closeSpy = vi.spyOn(wrapper.vm, 'close');
await wrapper.vm.onKeydown({ key: 'Enter' });
expect(closeSpy).toHaveBeenCalled();
});
});

View File

@ -1,4 +1,4 @@
import Vue, { Plugin } from 'vue';
import { Plugin } from 'vue';
import { ConfirmationOptions } from '../confirmationoptions';
declare const plugin: Plugin;

View File

@ -1,8 +1,8 @@
<template>
<CDialog v-model:visible="visible" role="alertdialog" class="p-confirm-dialog" :modal="true" :header="header" :blockScroll="blockScroll" :position="position" :breakpoints="breakpoints" :closeOnEscape="closeOnEscape" @update:visible="onHide">
<template v-if="!$slots.message">
<i :class="iconClass" />
<span class="p-confirm-dialog-message">{{ message }}</span>
<i v-if="confirmation.icon" :class="iconClass" />
<span :class="{ 'p-confirm-dialog-message': confirmation.icon }">{{ message }}</span>
</template>
<component v-else :is="$slots.message" :message="confirmation"></component>
<template #footer>

View File

@ -1,6 +1,6 @@
<template>
<Portal>
<transition name="p-confirm-popup" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave">
<transition name="p-confirm-popup" @enter="onEnter" @after-enter="onAfterEnter" @leave="onLeave" @after-leave="onAfterLeave">
<div v-if="visible" :ref="containerRef" v-focustrap role="alertdialog" :class="containerClass" :aria-modal="visible" @click="onOverlayClick" @keydown="onOverlayKeydown" v-bind="$attrs">
<template v-if="!$slots.message">
<div class="p-confirm-popup-content">
@ -35,7 +35,9 @@ export default {
data() {
return {
visible: false,
confirmation: null
confirmation: null,
autoFocusAccept: null,
autoFocusReject: null
};
},
target: null,
@ -129,13 +131,18 @@ export default {
}
},
onEnter(el) {
this.focus();
this.autoFocusAccept = this.confirmation.defaultFocus === undefined || this.confirmation.defaultFocus === 'accept' ? true : false;
this.autoFocusReject = this.confirmation.defaultFocus === 'reject' ? true : false;
this.bindOutsideClickListener();
this.bindScrollListener();
this.bindResizeListener();
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
},
onAfterEnter() {
this.focus();
},
onLeave() {
this.unbindOutsideClickListener();
this.unbindScrollListener();
@ -221,7 +228,7 @@ export default {
let focusTarget = this.container.querySelector('[autofocus]');
if (focusTarget) {
focusTarget.focus();
focusTarget.focus({ preventScroll: true }); // Firefox requires preventScroll
}
},
isTargetClicked(event) {
@ -276,12 +283,6 @@ export default {
},
rejectClass() {
return ['p-confirm-popup-reject p-button-sm', this.confirmation ? this.confirmation.rejectClass || 'p-button-text' : null];
},
autoFocusAccept() {
return this.confirmation.defaultFocus === undefined || this.confirmation.defaultFocus === 'accept' ? true : false;
},
autoFocusReject() {
return this.confirmation.defaultFocus === 'reject' ? true : false;
}
},
components: {

View File

@ -107,8 +107,8 @@ export default {
getItemKey(processedItem) {
return this.getItemId(processedItem);
},
getItemProp(processedItem, name) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name]) : undefined;
getItemProp(processedItem, name, params) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name], params) : undefined;
},
getItemLabel(processedItem) {
return this.getItemProp(processedItem, 'label');
@ -129,9 +129,7 @@ export default {
return ObjectUtils.isNotEmpty(processedItem.items);
},
onItemClick(event, processedItem) {
const command = this.getItemProp(processedItem, 'command');
command && command({ originalEvent: event, item: processedItem.item });
this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item });
this.$emit('item-click', { originalEvent: event, processedItem, isFocus: true });
},
onItemMouseEnter(event, processedItem) {

View File

@ -4,7 +4,7 @@ import { VirtualScrollerProps } from '../virtualscroller';
type DataTablePaginatorPositionType = 'top' | 'bottom' | 'both' | undefined;
type DataTableSortFieldType = string | ((item: any) => string) | undefined;
type DataTableSortFieldType = string | ((item: any) => string) | undefined | null;
type DataTableDataKeyType = string | ((item: any) => string) | undefined;
@ -684,7 +684,7 @@ export interface DataTableProps {
/**
* One or more field names to use in row grouping.
*/
groupRowsBy?: (field: string) => object | string[] | string | undefined;
groupRowsBy?: ((field: string) => object) | string[] | string | undefined;
/**
* Whether the row groups can be expandable.
*/

View File

@ -19,7 +19,7 @@
<button class="ql-italic" type="button"></button>
<button class="ql-underline" type="button"></button>
</span>
<span class="ql-formats">
<span :key="reRenderColorKey" class="ql-formats">
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
@ -69,10 +69,16 @@ export default {
editorStyle: null,
modules: null
},
data() {
return {
reRenderColorKey: 0
};
},
quill: null,
watch: {
modelValue(newValue, oldValue) {
if (newValue !== oldValue && this.quill && !this.quill.hasFocus()) {
this.reRenderColorKey++;
this.renderValue(newValue);
}
}

View File

@ -252,11 +252,11 @@ export interface FileUploadSlots {
/**
* Function to remove an uploaded file.
*/
removeUploadedFileCallback: () => void;
removeUploadedFileCallback: (index: number) => void;
/**
* Function to remove a file.
*/
removeFileCallback: () => void;
removeFileCallback: (index: number) => void;
/**
* Uploaded progress as number.
*/

View File

@ -62,6 +62,7 @@
:aria-setsize="ariaSetSize"
:aria-posinset="getAriaPosInset(getOptionIndex(i, getItemOptions))"
@click="onOptionSelect($event, option, getOptionIndex(i, getItemOptions))"
@mousedown="onOptionMouseDown($event, getOptionIndex(i, getItemOptions))"
@mousemove="onOptionMouseMove($event, getOptionIndex(i, getItemOptions))"
@touchend="onOptionTouchEnd()"
>
@ -254,7 +255,7 @@ export default {
},
onListFocus(event) {
this.focused = true;
this.focusedOptionIndex = this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
this.$emit('focus', event);
},
onListBlur(event) {
@ -332,6 +333,9 @@ export default {
this.optionTouched = false;
index !== -1 && (this.focusedOptionIndex = index);
},
onOptionMouseDown(event, index) {
this.changeFocusedOptionIndex(event, index);
},
onOptionMouseMove(event, index) {
if (this.focusOnHover) {
this.changeFocusedOptionIndex(event, index);

View File

@ -118,8 +118,8 @@ export default {
getItemKey(processedItem) {
return this.getItemId(processedItem);
},
getItemProp(processedItem, name) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name]) : undefined;
getItemProp(processedItem, name, params) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name], params) : undefined;
},
getItemLabel(processedItem) {
return this.getItemProp(processedItem, 'label');
@ -140,9 +140,7 @@ export default {
return ObjectUtils.isNotEmpty(processedItem.items);
},
onItemClick(event, processedItem) {
const command = this.getItemProp(processedItem, 'command');
command && command({ originalEvent: event, item: processedItem.item });
this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item });
this.$emit('item-click', { originalEvent: event, processedItem, isFocus: true });
},
onItemMouseEnter(event, processedItem) {

View File

@ -109,8 +109,8 @@ export default {
getItemKey(processedItem) {
return this.getItemId(processedItem);
},
getItemProp(processedItem, name) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name]) : undefined;
getItemProp(processedItem, name, params) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name], params) : undefined;
},
getItemLabel(processedItem) {
return this.getItemProp(processedItem, 'label');
@ -131,9 +131,7 @@ export default {
return ObjectUtils.isNotEmpty(processedItem.items);
},
onItemClick(event, processedItem) {
const command = this.getItemProp(processedItem, 'command');
command && command({ originalEvent: event, item: processedItem.item });
this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item });
this.$emit('item-click', { originalEvent: event, processedItem, isFocus: true });
},
onItemMouseEnter(event, processedItem) {

View File

@ -660,8 +660,12 @@ export default {
pressedInInputText && (this.focusedOptionIndex = -1);
},
onHomeKey(event, pressedInInputText = false) {
const { currentTarget } = event;
if (pressedInInputText) {
event.currentTarget.setSelectionRange(0, 0);
const len = currentTarget.value.length;
currentTarget.setSelectionRange(0, event.shiftKey ? len : 0);
this.focusedOptionIndex = -1;
} else {
let metaKey = event.metaKey || event.ctrlKey;
@ -679,11 +683,12 @@ export default {
event.preventDefault();
},
onEndKey(event, pressedInInputText = false) {
if (pressedInInputText) {
const target = event.currentTarget;
const len = target.value.length;
const { currentTarget } = event;
target.setSelectionRange(len, len);
if (pressedInInputText) {
const len = currentTarget.value.length;
currentTarget.setSelectionRange(event.shiftKey ? 0 : len, len);
this.focusedOptionIndex = -1;
} else {
let metaKey = event.metaKey || event.ctrlKey;

View File

@ -31,7 +31,7 @@
v-bind="listProps"
>
<template v-for="(item, i) of modelValue" :key="getItemKey(item, i)">
<li :id="id + '_' + i" v-ripple role="option" :class="itemClass(item, `${id}_${i}`)" @click="onItemClick($event, item, i)" @touchend="onItemTouchEnd" :aria-selected="isSelected(item)">
<li :id="id + '_' + i" v-ripple role="option" :class="itemClass(item, `${id}_${i}`)" @click="onItemClick($event, item, i)" @touchend="onItemTouchEnd" :aria-selected="isSelected(item)" @mousedown="onOptionMouseDown(i)">
<slot name="item" :item="item" :index="i"> </slot>
</li>
</template>
@ -148,9 +148,12 @@ export default {
},
onListFocus(event) {
const selectedFirstItem = DomHandler.findSingle(this.list, 'li.p-orderlist-item.p-highlight');
const index = selectedFirstItem ? ObjectUtils.findIndexInList(selectedFirstItem, this.list.children) : '0';
const findIndex = ObjectUtils.findIndexInList(selectedFirstItem, this.list.children);
this.focused = true;
const index = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : selectedFirstItem ? findIndex : -1;
this.changeFocusedOptionIndex(index);
this.$emit('focus', event);
},
@ -195,6 +198,10 @@ export default {
break;
}
},
onOptionMouseDown(index) {
this.focused = true;
this.focusedOptionIndex = index;
},
onArrowDownKey(event) {
const optionIndex = this.findNextOptionIndex(this.focusedOptionIndex);

View File

@ -47,7 +47,7 @@ export default {
collapsed: Boolean,
toggleButtonProps: {
type: null,
defaault: null
default: null
}
},
data() {

5
components/panelmenu/PanelMenu.vue Executable file → Normal file
View File

@ -201,11 +201,10 @@ export default {
},
changeActiveItem(event, item, selfActive = false) {
if (!this.isItemDisabled(item)) {
this.activeItem = selfActive ? item : this.activeItem && ObjectUtils.equals(item, this.activeItem) ? null : item;
const active = this.isItemActive(item);
const eventName = active ? 'panel-open' : 'panel-close';
const eventName = !active ? 'panel-open' : 'panel-close';
this.activeItem = selfActive ? item : this.activeItem && this.activeItem === item ? null : item;
this.changeExpandedKeys({ item, expanded: !active });
this.$emit(eventName, { originalEvent: event, item });
}

View File

@ -96,8 +96,8 @@ export default {
getItemKey(processedItem) {
return this.getItemId(processedItem);
},
getItemProp(processedItem, name) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name]) : undefined;
getItemProp(processedItem, name, params) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name], params) : undefined;
},
getItemLabel(processedItem) {
return this.getItemProp(processedItem, 'label');
@ -118,9 +118,7 @@ export default {
return ObjectUtils.isNotEmpty(processedItem.items);
},
onItemClick(event, processedItem) {
const command = this.getItemProp(processedItem, 'command');
command && command({ originalEvent: event, item: processedItem.item });
this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item });
this.$emit('item-toggle', { processedItem, expanded: !this.isItemActive(processedItem) });
},
onItemToggle(event) {

View File

@ -36,6 +36,7 @@
@click="onItemClick($event, item, i, 0)"
@dblclick="onItemDblClick($event, item, 0)"
@touchend="onItemTouchEnd"
@mousedown="onOptionMouseDown(i, 'sourceList')"
role="option"
:aria-selected="isSelected(item, 0)"
>
@ -80,6 +81,7 @@
@click="onItemClick($event, item, i, 1)"
@dblclick="onItemDblClick($event, item, 1)"
@keydown="onItemKeyDown($event, 'targetList')"
@mousedown="onOptionMouseDown(i, 'targetList')"
@touchend="onItemTouchEnd"
role="option"
:aria-selected="isSelected(item, 1)"
@ -236,9 +238,12 @@ export default {
},
onListFocus(event, listType) {
const selectedFirstItem = DomHandler.findSingle(this.$refs[listType].$el, 'li.p-picklist-item.p-highlight');
const index = selectedFirstItem ? ObjectUtils.findIndexInList(selectedFirstItem, this.$refs[listType].$el.children) : '0';
const findIndex = ObjectUtils.findIndexInList(selectedFirstItem, this.$refs[listType].$el.children);
this.focused[listType] = true;
const index = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : selectedFirstItem ? findIndex : -1;
this.changeFocusedOptionIndex(index, listType);
this.$emit('focus', event);
},
@ -247,6 +252,10 @@ export default {
this.focusedOptionIndex = -1;
this.$emit('blur', event);
},
onOptionMouseDown(index, listType) {
this.focused[listType] = true;
this.focusedOptionIndex = index;
},
moveUp(event, listIndex) {
if (this.d_selection && this.d_selection[listIndex]) {
let valueList = [...this.modelValue[listIndex]];

View File

@ -18,17 +18,74 @@ describe('RadioButton.vue', () => {
expect(wrapper.find('input').attributes().type).toBe('radio');
});
it('should clicked', async () => {
await wrapper.vm.onClick({});
it('When disabled true and onClick triggered click emit should not be called', async () => {
await wrapper.setProps({ disabled: true });
await wrapper.vm.onClick();
expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['Tatooine']);
expect(wrapper.emitted().change[0]).toEqual([{}]);
expect(wrapper.emitted()['click']).toEqual(undefined);
expect(wrapper.emitted()['update:modelValue']).toEqual(undefined);
});
it('should checked', async () => {
it('When disabled false and onClick triggered click emit should be called', async () => {
await wrapper.vm.onClick();
expect(wrapper.emitted()['update:modelValue'].length).toEqual(1);
expect(wrapper.emitted().change.length).toEqual(1);
});
it('When value and modelValue equal and onClick triggered change emit should not be called', async () => {
await wrapper.setProps({ modelValue: 'test', value: 'test' });
await wrapper.vm.onClick();
expect(wrapper.emitted()['change']).toEqual(undefined);
});
it('When modelValue changed, Checked should be effected', async () => {
await wrapper.setProps({ modelValue: 'Tatooine' });
expect(wrapper.vm.checked).toBe(true);
expect(wrapper.find('.p-radiobutton').classes()).toContain('p-radiobutton-checked');
});
it('When component cliked OnClick method should be called', async () => {
const spy = vi.spyOn(wrapper.vm, 'onClick');
await wrapper.find('.p-radiobutton').trigger('click');
expect(spy).toHaveBeenCalled();
});
it('When component focused onFocus method should be called', async () => {
await wrapper.setProps({ inputId: 'test' });
const spy = vi.spyOn(wrapper.vm, 'onFocus');
await wrapper.find('#test').trigger('focus');
expect(spy).toHaveBeenCalled();
});
it('When onFocus method triggered, false should be true', async () => {
await wrapper.vm.onFocus();
expect(wrapper.vm.focused).toBeTruthy();
expect(wrapper.emitted().focus.length).toEqual(1);
});
it('When component blur onBlur method should be called', async () => {
await wrapper.setProps({ inputId: 'test' });
const blurSpy = vi.spyOn(wrapper.vm, 'onBlur');
await wrapper.find('#test').trigger('blur');
expect(blurSpy).toHaveBeenCalled();
});
it('When onBlur method triggered, false should be false', async () => {
await wrapper.vm.onBlur();
expect(wrapper.vm.focus).toBeFalsy();
expect(wrapper.emitted().blur.length).toEqual(1);
});
});

View File

@ -1,3 +1,4 @@
import { VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
export interface RatingChangeEvent {

View File

@ -44,6 +44,10 @@ export interface SidebarProps {
* Default value is true.
*/
modal?: boolean | undefined;
/**
* Whether background scroll should be blocked when sidebar is visible.
*/
blockScroll?: boolean | undefined;
}
export interface SidebarSlots {

View File

@ -2,7 +2,6 @@ import { mount } from '@vue/test-utils';
import PrimeVue from 'primevue/config';
import { describe, expect, it } from 'vitest';
import Sidebar from './Sidebar.vue';
describe('Sidebar.vue', () => {
let wrapper;
@ -15,7 +14,7 @@ describe('Sidebar.vue', () => {
}
},
props: {
visible: true,
visible: false,
bazeZIndex: 1000
},
slots: {
@ -23,95 +22,76 @@ describe('Sidebar.vue', () => {
header: `<span class="header">Header Template</span>`
}
});
wrapper.setProps({ visible: true });
});
it('should exist', () => {
afterEach(() => {
vi.clearAllMocks();
});
it('When component is mounted, sidebar should be exist', () => {
expect(wrapper.find('.p-sidebar.p-component').exists()).toBe(true);
expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-left');
expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-active');
});
it('should close', async () => {
await wrapper.vm.hide();
it('When mask element triggered, sidebar should be hide', async () => {
const unbindOutsideClickListenerSpy = vi.spyOn(wrapper.vm, 'unbindOutsideClickListener');
expect(wrapper.emitted()['update:visible'][0]).toEqual([false]);
await wrapper.find('.p-sidebar-mask').trigger('mousedown');
await wrapper.setProps({ visible: false });
expect(wrapper.find('.p-sidebar.p-component').exists()).toBe(false);
expect(wrapper.emitted()['update:visible'].length).toBe(1);
expect(unbindOutsideClickListenerSpy).toHaveBeenCalled();
});
it('should set position', async () => {
await wrapper.setProps({ position: 'bottom' });
it('When transition trigger to onEnter, sidebar should be visible', async () => {
const focusSpy = vi.spyOn(wrapper.vm, 'focus');
expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-bottom');
await wrapper.vm.onEnter();
expect(wrapper.emitted().show.length).toBe(1);
expect(wrapper.vm.maskVisible).toBeTruthy();
expect(focusSpy).toHaveBeenCalled();
});
it('should set position', async () => {
await wrapper.setProps({ position: 'full' });
it('When transition trigger to onLeave, unbindOutsideClickListener should be triggered', async () => {
const unbindOutsideClickListenerSpy = vi.spyOn(wrapper.vm, 'unbindOutsideClickListener');
expect(wrapper.vm.fullScreen).toBe(true);
expect(wrapper.find('.p-sidebar').classes()).toContain('p-sidebar-full');
await wrapper.vm.onLeave();
expect(wrapper.emitted().hide.length).toBe(1);
expect(unbindOutsideClickListenerSpy).toHaveBeenCalled();
});
it('should have custom close icon when provided', async () => {
await wrapper.setProps({ closeIcon: 'pi pi-discord' });
const icon = wrapper.find('.p-sidebar-close-icon');
it('When transition trigger to onAfterEnter, bindOutsideClickListener should be triggered', async () => {
const bindOutsideClickListenerSpy = vi.spyOn(wrapper.vm, 'bindOutsideClickListener');
expect(icon.classes()).toContain('pi-discord');
await wrapper.vm.onAfterEnter();
expect(bindOutsideClickListenerSpy).toHaveBeenCalled();
});
it('should header slot rendered', () => {
expect(wrapper.find('.p-sidebar-header').exists()).toBe(true);
expect(wrapper.find('.p-sidebar-header-content').exists()).toBe(true);
expect(wrapper.find('span.header').exists()).toBe(true);
expect(wrapper.find('span.header').text()).toBe('Header Template');
it('When keydown is triggered , hide method should be triggered', async () => {
const hideSpy = vi.spyOn(wrapper.vm, 'hide');
await wrapper.find('.p-sidebar').trigger('keydown', { code: 'Escape' });
expect(hideSpy).toHaveBeenCalled();
});
it('should default slot rendered', () => {
expect(wrapper.find('h3').exists()).toBe(true);
expect(wrapper.find('h3').text()).toBe('Left Sidebar');
it('When keydown is triggered , hide method should be triggered', async () => {
const hideSpy = vi.spyOn(wrapper.vm, 'hide');
await wrapper.find('.p-sidebar-close').trigger('click');
expect(hideSpy).toHaveBeenCalled();
});
it('should keydown work', async () => {
const event = { code: 'Escape' };
it('When component is unmount , unbindOutsideClickListenerSpy method should be triggered', async () => {
const unbindOutsideClickListenerSpy = vi.spyOn(wrapper.vm, 'unbindOutsideClickListener');
await wrapper.vm.onKeydown(event);
await wrapper.unmount();
expect(wrapper.emitted()['update:visible'][0]).toEqual([false]);
});
});
describe('when visible is false', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Sidebar, {
global: {
plugins: [PrimeVue],
stubs: {
teleport: true,
transition: false
}
},
props: {
visible: true,
bazeZIndex: 1000
}
});
});
it('should show and hide emit work', async () => {
expect(wrapper.emitted()['show'][0]).toEqual([]);
await wrapper.setProps({ visible: false });
expect(wrapper.emitted()['hide'][0]).toEqual([]);
});
it('should be destroyed', () => {
wrapper.unmount();
expect(wrapper.componentVM.container).toBe(null);
expect(wrapper.componentVM.mask).toBe(null);
expect(unbindOutsideClickListenerSpy).toHaveBeenCalled();
expect(Sidebar.container).toBe(null);
});
});

View File

@ -1,20 +1,22 @@
<template>
<Portal>
<transition name="p-sidebar" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave" appear>
<div v-if="visible" :ref="containerRef" v-focustrap :class="containerClass" role="complementary" :aria-modal="modal" @keydown="onKeydown" v-bind="$attrs">
<div :ref="headerContainerRef" class="p-sidebar-header">
<div v-if="$slots.header" class="p-sidebar-header-content">
<slot name="header"></slot>
<div v-if="maskVisible" ref="mask" style="maskStyle" :class="maskClasses" @mousedown="onMaskClick">
<transition name="p-sidebar" @after-enter="onAfterEnter" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave" appear>
<div :ref="containerRef" v-focustrap :class="containerClass" role="complementary" :aria-modal="modal" @keydown="onKeydown" v-bind="$attrs">
<div :ref="headerContainerRef" class="p-sidebar-header">
<div v-if="$slots.header" class="p-sidebar-header-content">
<slot name="header"></slot>
</div>
<button v-if="showCloseIcon" :ref="closeButtonRef" v-ripple autofocus type="button" class="p-sidebar-close p-sidebar-icon p-link" :aria-label="closeAriaLabel" @click="hide">
<span :class="['p-sidebar-close-icon', closeIcon]" />
</button>
</div>
<div :ref="contentRef" class="p-sidebar-content">
<slot></slot>
</div>
<button v-if="showCloseIcon" :ref="closeButtonRef" v-ripple autofocus type="button" class="p-sidebar-close p-sidebar-icon p-link" :aria-label="closeAriaLabel" @click="hide">
<span :class="['p-sidebar-close-icon', closeIcon]" />
</button>
</div>
<div :ref="contentRef" class="p-sidebar-content">
<slot></slot>
</div>
</div>
</transition>
</transition>
</div>
</Portal>
</template>
@ -26,7 +28,6 @@ import { DomHandler, ZIndexUtils } from 'primevue/utils';
export default {
name: 'Sidebar',
inheritAttrs: false,
emits: ['update:visible', 'show', 'hide'],
props: {
visible: {
@ -60,50 +61,69 @@ export default {
modal: {
type: Boolean,
default: true
},
blockScroll: {
type: Boolean,
default: true
}
},
mask: null,
maskClickListener: null,
container: null,
content: null,
headerContainer: null,
closeButton: null,
beforeUnmount() {
this.destroyModal();
outsideClickListener: null,
data() {
return {
maskVisible: false
};
},
watch: {
visible(val) {
this.maskVisible = val;
}
},
beforeUnmount() {
if (this.container && this.autoZIndex) {
ZIndexUtils.clear(this.container);
}
this.unbindOutsideClickListener();
this.container = null;
},
methods: {
hide() {
this.$emit('update:visible', false);
this.unbindOutsideClickListener();
this.blockScroll && DomHandler.removeClass(document.body, 'p-overflow-hidden');
},
onEnter(el) {
onEnter() {
this.$emit('show');
if (this.autoZIndex) {
ZIndexUtils.set('modal', el, this.baseZIndex || this.$primevue.config.zIndex.modal);
ZIndexUtils.set('modal', this.$refs.mask, this.baseZIndex || this.$primevue.config.zIndex.modal);
}
this.maskVisible = true;
this.focus();
if (this.modal && !this.fullScreen) {
this.enableModality();
}
},
onLeave() {
this.$emit('hide');
DomHandler.addClass(this.$refs.mask, 'p-component-overlay-leave');
if (this.modal && !this.fullScreen) {
this.disableModality();
this.$emit('hide');
this.unbindOutsideClickListener();
},
onAfterLeave() {
if (this.autoZIndex) {
ZIndexUtils.clear(this.mask);
}
},
onAfterLeave(el) {
if (this.autoZIndex) {
ZIndexUtils.clear(el);
onAfterEnter() {
this.bindOutsideClickListener();
if (this.blockScroll) {
DomHandler.addClass(document.body, 'p-overflow-hidden');
}
},
focus() {
@ -123,54 +143,14 @@ export default {
focusTarget && focusTarget.focus();
},
enableModality() {
if (!this.mask) {
this.mask = document.createElement('div');
this.mask.setAttribute('class', 'p-sidebar-mask p-component-overlay p-component-overlay-enter');
this.mask.style.zIndex = String(parseInt(this.container.style.zIndex, 10) - 1);
if (this.dismissable) {
this.bindMaskClickListener();
}
document.body.appendChild(this.mask);
DomHandler.addClass(document.body, 'p-overflow-hidden');
}
},
disableModality() {
if (this.mask) {
DomHandler.addClass(this.mask, 'p-component-overlay-leave');
this.mask.addEventListener('animationend', () => {
this.destroyModal();
});
}
},
bindMaskClickListener() {
if (!this.maskClickListener) {
this.maskClickListener = () => {
this.hide();
};
this.mask.addEventListener('click', this.maskClickListener);
}
},
onKeydown(event) {
if (event.code === 'Escape') {
this.hide();
}
},
unbindMaskClickListener() {
if (this.maskClickListener) {
this.mask.removeEventListener('click', this.maskClickListener);
this.maskClickListener = null;
}
},
destroyModal() {
if (this.mask) {
this.unbindMaskClickListener();
document.body.removeChild(this.mask);
DomHandler.removeClass(document.body, 'p-overflow-hidden');
this.mask = null;
onMaskClick(event) {
if (this.dismissable && this.modal && this.$refs.mask === event.target) {
this.hide();
}
},
containerRef(el) {
@ -184,16 +164,43 @@ export default {
},
closeButtonRef(el) {
this.closeButton = el;
},
getPositionClass() {
const positions = ['left', 'right', 'top', 'bottom'];
const pos = positions.find((item) => item === this.position);
return pos ? `p-sidebar-${pos}` : '';
},
bindOutsideClickListener() {
if (!this.outsideClickListener) {
this.outsideClickListener = (event) => {
if (!this.modal && this.isOutsideClicked(event) && this.dismissable) {
this.hide();
}
};
document.addEventListener('click', this.outsideClickListener);
}
},
unbindOutsideClickListener() {
if (this.outsideClickListener) {
document.removeEventListener('click', this.outsideClickListener);
this.outsideClickListener = null;
}
},
isOutsideClicked(event) {
return this.container && !this.container.contains(event.target);
}
},
computed: {
containerClass() {
return [
'p-sidebar p-component p-sidebar-' + this.position,
'p-sidebar p-component',
this.getPositionClass(),
{
'p-sidebar-active': this.visible,
'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false
'p-ripple-disabled': this.$primevue.config.ripple === false,
'p-sidebar-full': this.fullScreen
}
];
},
@ -202,6 +209,18 @@ export default {
},
closeAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.close : undefined;
},
maskClasses() {
return [
'p-sidebar-mask',
this.getPositionClass(),
{
'p-component-overlay p-component-overlay-enter': this.modal,
'p-sidebar-mask-scrollblocker': this.blockScroll,
'p-sidebar-visible': this.maskVisible,
'p-sidebar-full': this.fullScreen
}
];
}
},
directives: {
@ -215,134 +234,183 @@ export default {
</script>
<style>
.p-sidebar {
.p-sidebar-mask {
position: fixed;
transition: transform 0.3s;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
justify-content: center;
align-items: center;
pointer-events: none;
background-color: transparent;
transition-property: background-color;
}
.p-sidebar-visible {
display: flex;
}
.p-sidebar-mask.p-component-overlay {
pointer-events: auto;
}
.p-sidebar {
display: flex;
flex-direction: column;
pointer-events: auto;
transform: translate3d(0px, 0px, 0px);
position: relative;
transition: transform 0.3s;
}
.p-sidebar-content {
position: relative;
overflow-y: auto;
flex-grow: 1;
}
.p-sidebar-header {
display: flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
}
.p-sidebar-icon {
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
position: relative;
}
.p-sidebar-left {
top: 0;
left: 0;
width: 20rem;
height: 100%;
}
.p-sidebar-right {
top: 0;
right: 0;
width: 20rem;
height: 100%;
}
.p-sidebar-top {
top: 0;
left: 0;
width: 100%;
height: 10rem;
}
.p-sidebar-bottom {
bottom: 0;
left: 0;
width: 100%;
height: 10rem;
}
.p-sidebar-full {
width: 100%;
height: 100%;
top: 0;
left: 0;
-webkit-transition: none;
.p-sidebar-full .p-sidebar {
transition: none;
transform: none;
width: 100vw !important;
height: 100vh !important;
max-height: 100%;
top: 0px !important;
left: 0px !important;
}
/* Animation */
/* Center */
.p-sidebar-left.p-sidebar-enter-from,
.p-sidebar-left.p-sidebar-leave-to {
transform: translateX(-100%);
}
.p-sidebar-right.p-sidebar-enter-from,
.p-sidebar-right.p-sidebar-leave-to {
transform: translateX(100%);
}
.p-sidebar-top.p-sidebar-enter-from,
.p-sidebar-top.p-sidebar-leave-to {
transform: translateY(-100%);
}
.p-sidebar-bottom.p-sidebar-enter-from,
.p-sidebar-bottom.p-sidebar-leave-to {
transform: translateY(100%);
}
.p-sidebar-full.p-sidebar-enter-from,
.p-sidebar-full.p-sidebar-leave-to {
opacity: 0;
}
.p-sidebar-full.p-sidebar-enter-active,
.p-sidebar-full.p-sidebar-leave-active {
transition: opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1);
}
.p-sidebar-left.p-sidebar-sm,
.p-sidebar-right.p-sidebar-sm {
/* Position */
.p-sidebar-left {
justify-content: flex-start;
}
.p-sidebar-right {
justify-content: flex-end;
}
.p-sidebar-top {
align-items: flex-start;
}
.p-sidebar-bottom {
align-items: flex-end;
}
/* Size */
.p-sidebar-left .p-sidebar {
width: 20rem;
height: 100%;
}
.p-sidebar-right .p-sidebar {
width: 20rem;
height: 100%;
}
.p-sidebar-top .p-sidebar {
height: 10rem;
width: 100%;
}
.p-sidebar-bottom .p-sidebar {
height: 10rem;
width: 100%;
}
.p-sidebar-left .p-sidebar-sm,
.p-sidebar-right .p-sidebar-sm {
width: 20rem;
}
.p-sidebar-left.p-sidebar-md,
.p-sidebar-right.p-sidebar-md {
.p-sidebar-left .p-sidebar-md,
.p-sidebar-right .p-sidebar-md {
width: 40rem;
}
.p-sidebar-left.p-sidebar-lg,
.p-sidebar-right.p-sidebar-lg {
.p-sidebar-left .p-sidebar-lg,
.p-sidebar-right .p-sidebar-lg {
width: 60rem;
}
.p-sidebar-top.p-sidebar-sm,
.p-sidebar-bottom.p-sidebar-sm {
.p-sidebar-top .p-sidebar-sm,
.p-sidebar-bottom .p-sidebar-sm {
height: 10rem;
}
.p-sidebar-top.p-sidebar-md,
.p-sidebar-bottom.p-sidebar-md {
.p-sidebar-top .p-sidebar-md,
.p-sidebar-bottom .p-sidebar-md {
height: 20rem;
}
.p-sidebar-top.p-sidebar-lg,
.p-sidebar-bottom.p-sidebar-lg {
.p-sidebar-top .p-sidebar-lg,
.p-sidebar-bottom .p-sidebar-lg {
height: 30rem;
}
.p-sidebar-left .p-sidebar-view,
.p-sidebar-right .p-sidebar-view,
.p-sidebar-top .p-sidebar-view,
.p-sidebar-bottom .p-sidebar-view {
width: 100%;
height: 100%;
}
.p-sidebar-left .p-sidebar-content,
.p-sidebar-right .p-sidebar-content,
.p-sidebar-top .p-sidebar-content,
.p-sidebar-bottom .p-sidebar-content {
width: 100%;
height: 100%;
}
@media screen and (max-width: 64em) {
.p-sidebar-left.p-sidebar-lg,
.p-sidebar-left.p-sidebar-md,
.p-sidebar-right.p-sidebar-lg,
.p-sidebar-right.p-sidebar-md {
.p-sidebar-left .p-sidebar-lg,
.p-sidebar-left .p-sidebar-md,
.p-sidebar-right .p-sidebar-lg,
.p-sidebar-right .p-sidebar-md {
width: 20rem;
}
}

View File

@ -536,6 +536,9 @@ export default {
.p-speeddial {
position: absolute;
display: flex;
}
.p-speeddial-button {
z-index: 1;
}
@ -548,6 +551,7 @@ export default {
justify-content: center;
transition: top 0s linear 0.2s;
pointer-events: none;
z-index: 2;
}
.p-speeddial-item {

View File

@ -27,6 +27,7 @@ import { UniqueComponentId } from 'primevue/utils';
export default {
name: 'SplitButton',
emits: ['click'],
props: {
label: {
type: String,
@ -91,6 +92,8 @@ export default {
},
onDefaultButtonClick(event) {
this.$refs.menu.hide(event);
this.$emit('click');
}
},
computed: {

View File

@ -21,7 +21,7 @@
<span class="p-menuitem-text">{{ label(item) }}</span>
</a>
</template>
<component v-else :is="$slots.item" :item="item"></component>
<component v-else :is="$slots.item" :item="item" :index="i"></component>
</li>
</router-link>
<li v-else-if="visible(item)" ref="tab" :class="getItemClass(item, i)" role="presentation" @click="onItemClick($event, item, i)" @keydown="onKeydownItem($event, item, i)">
@ -31,7 +31,7 @@
<span class="p-menuitem-text">{{ label(item) }}</span>
</a>
</template>
<component v-else :is="$slots.item" :item="item"></component>
<component v-else :is="$slots.item" :item="item" :index="i"></component>
</li>
</template>
<li ref="inkbar" class="p-tabmenu-ink-bar"></li>

View File

@ -99,11 +99,11 @@ export default {
},
previousButtonProps: {
type: null,
defaault: null
default: null
},
nextButtonProps: {
type: null,
defaault: null
default: null
}
},
data() {

View File

@ -96,8 +96,8 @@ export default {
getItemKey(processedItem) {
return this.getItemId(processedItem);
},
getItemProp(processedItem, name) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name]) : undefined;
getItemProp(processedItem, name, params) {
return processedItem && processedItem.item ? ObjectUtils.getItemValue(processedItem.item[name], params) : undefined;
},
getItemLabel(processedItem) {
return this.getItemProp(processedItem, 'label');
@ -118,9 +118,7 @@ export default {
return ObjectUtils.isNotEmpty(processedItem.items);
},
onItemClick(event, processedItem) {
const command = this.getItemProp(processedItem, 'command');
command && command({ originalEvent: event, item: processedItem.item });
this.getItemProp(processedItem, 'command', { originalEvent: event, item: processedItem.item });
this.$emit('item-click', { originalEvent: event, processedItem, isFocus: true });
},
onItemMouseEnter(event, processedItem) {

View File

@ -562,6 +562,10 @@ export default {
<td>loader</td>
<td>options: Options of the loader items for virtualscroller</td>
</tr>
<tr>
<td>empty</td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
@ -605,6 +609,10 @@ export default {
<td>p-autocomplete-token-label</td>
<td>Label of a selected item in multiple mode.</td>
</tr>
<tr>
<td>p-autocomplete-empty-message</td>
<td>Container element when there is no suggestion to display.</td>
</tr>
<tr>
<td>p-overlay-open</td>
<td>Container element when overlay is visible.</td>

View File

@ -681,7 +681,7 @@ export default {
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" class="mr-2" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<PanelMenu :model="items" :expandedKeys="expandedKeys" />
<PanelMenu :model="items" v-model:expandedKeys="expandedKeys" />
</div>
</template>
@ -867,7 +867,7 @@ export default {
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" class="mr-2" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<PanelMenu :model="items" :expandedKeys="expandedKeys" />
<PanelMenu :model="items" v-model:expandedKeys="expandedKeys" />
</div>
</template>
@ -1053,7 +1053,7 @@ export default {
<p-button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" class="mr-2"></p-button>
<p-button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll"></p-button>
</div>
<p-panelmenu :model="items" :expanded-keys="expandedKeys"></p-panelmenu>
<p-panelmenu :model="items" v-model:expanded-keys="expandedKeys"></p-panelmenu>
</div>
<script type="module">

View File

@ -121,6 +121,12 @@ import Sidebar from 'primevue/sidebar';
<b> Deprecated: </b> <i>aria.close</i> can be used in defaults to PrimeVue <router-link to="/locale">Locale</router-link> configuration.
</td>
</tr>
<tr>
<td>blockScroll</td>
<td>boolean</td>
<td>true</td>
<td>Whether background scroll should be blocked when sidebar is visible.</td>
</tr>
</tbody>
</table>
</div>

View File

@ -159,7 +159,10 @@ export default {
<tbody>
<tr>
<td>item</td>
<td>item: Menuitem instance</td>
<td>
item: Menuitem instance<br />
index: Index of the menuitem instance
</td>
</tr>
</tbody>
</table>

View File

@ -212,7 +212,7 @@ export default class NodeService {
&lt;Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" /&gt;
&lt;Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" /&gt;
&lt;/div&gt;
&lt;Tree :value="nodes" :expandedKeys="expandedKeys"&gt;&lt;/Tree&gt;
&lt;Tree :value="nodes" v-model:expandedKeys="expandedKeys"&gt;&lt;/Tree&gt;
</code></pre>
@ -894,7 +894,7 @@ export default {
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<Tree :value="nodes" :expandedKeys="expandedKeys"></Tree>
<Tree :value="nodes" v-model:expandedKeys="expandedKeys"></Tree>
</div>
</template>
@ -958,7 +958,7 @@ export default {
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<Tree :value="nodes" :expandedKeys="expandedKeys"></Tree>
<Tree :value="nodes" v-model:expandedKeys="expandedKeys"></Tree>
</div>
</template>
@ -1018,7 +1018,7 @@ export default {
<p-button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll"></p-button>
<p-button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll"></p-button>
</div>
<p-tree :value="nodes" :expanded-keys="expandedKeys"></p-tree>
<p-tree :value="nodes" v-model:expanded-keys="expandedKeys"></p-tree>
</div>
<script type="module">

View File

@ -23,7 +23,7 @@
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<Tree :value="nodes" :expandedKeys="expandedKeys"></Tree>
<Tree v-model:expandedKeys="expandedKeys" :value="nodes"></Tree>
</div>
</div>

View File

@ -647,7 +647,7 @@ export default {
&lt;Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" /&gt;
&lt;Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" /&gt;
&lt;/div&gt;
&lt;TreeTable :value="nodes" :expandedKeys="expandedKeys"&gt;
&lt;TreeTable :value="nodes" v-model:expandedKeys="expandedKeys"&gt;
&lt;Column field="name" header="Name" :expander="true"&gt;&lt;/Column&gt;
&lt;Column field="size" header="Size"&gt;&lt;/Column&gt;
&lt;Column field="type" header="Type"&gt;&lt;/Column&gt;
@ -1917,7 +1917,7 @@ export default {
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<TreeTable :value="nodes" :expandedKeys="expandedKeys">
<TreeTable :value="nodes" v-model:expandedKeys="expandedKeys">
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
@ -2008,7 +2008,7 @@ button {
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<TreeTable :value="nodes" :expandedKeys="expandedKeys">
<TreeTable :value="nodes" v-model:expandedKeys="expandedKeys">
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>
@ -2093,7 +2093,7 @@ button {
<p-button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll"></p-button>
<p-button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll"></p-button>
</div>
<p-treetable :value="nodes" :expanded-keys="expandedKeys">
<p-treetable :value="nodes" v-model:expanded-keys="expandedKeys">
<p-column field="name" header="Name" :expander="true"></p-column>
<p-column field="size" header="Size"></p-column>
<p-column field="type" header="Type"></p-column>

View File

@ -36,7 +36,7 @@
<Button type="button" icon="pi pi-plus" label="Expand All" @click="expandAll" />
<Button type="button" icon="pi pi-minus" label="Collapse All" @click="collapseAll" />
</div>
<TreeTable :value="nodes" :expandedKeys="expandedKeys">
<TreeTable v-model:expandedKeys="expandedKeys" :value="nodes">
<Column field="name" header="Name" :expander="true"></Column>
<Column field="size" header="Size"></Column>
<Column field="type" header="Type"></Column>