Fixed #3802 - Improve folder structure for nuxt configurations

This commit is contained in:
mertsincan 2023-03-26 06:22:57 +01:00
parent 851950270b
commit f5fe822afb
563 changed files with 1703 additions and 1095 deletions

179
components/lib/toast/Toast.d.ts vendored Executable file
View file

@ -0,0 +1,179 @@
/**
*
* Toast is used to display messages in an overlay.
*
* [Live Demo](https://www.primevue.org/toast/)
*
* @module toast
*
*/
import { ButtonHTMLAttributes, VNode } from 'vue';
import { ClassComponent, GlobalComponentConstructor } from '../ts-helpers';
/**
* Defines message options in Toast component.
*/
export interface ToastMessageOptions {
/**
* Severity level of the message.
* @defaultValue info
*/
severity?: 'success' | 'info' | 'warn' | 'error' | undefined;
/**
* Summary content of the message.
*/
summary?: string | undefined;
/**
* Detail content of the message.
*/
detail?: any | undefined;
/**
* Whether the message can be closed manually using the close icon.
* @defaultValue true
*/
closable?: boolean | undefined;
/**
* Delay in milliseconds to close the message automatically.
*/
life?: number | undefined;
/**
* Key of the Toast to display the message.
*/
group?: string | undefined;
/**
* Style class of the message.
*/
styleClass?: any;
/**
* Style class of the content.
*/
contentStyleClass?: any;
}
/**
* Defines breakpoints type in Toast component.
*/
export interface ToastBreakpointsType {
/**
* Breakpoint for responsive mode.
*
* Example:
*
* <Toast :breakpoints="{'960px': { width: '75vw', ... }" ... />
*
*/
[key: string]: any;
}
/**
* Defines valid properties in Toast component.
*/
export interface ToastProps {
/**
* Unique identifier of a message group.
*/
group?: string | undefined;
/**
* Position of the toast in viewport.
* @defaultValue top-right
*/
position?: 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'center' | undefined;
/**
* Whether to automatically manage layering.
* @defaultValue true
*/
autoZIndex?: boolean | undefined;
/**
* Base zIndex value to use in layering.
* @defaultValue 0
*/
baseZIndex?: number | undefined;
/**
* Object literal to define styles per screen size.
* @see ToastBreakpointsType
*/
breakpoints?: ToastBreakpointsType;
/**
* Icon to display in the toast close button.
* @defaultValue pi pi-times
*/
closeIcon?: string | undefined;
/**
* Icon to display in the toast with info severity.
* @defaultValue pi pi-info-circle
*/
infoIcon?: string | undefined;
/**
* Icon to display in the toast with warn severity.
* @defaultValue pi pi-exclamation-triangle
*/
warnIcon?: string | undefined;
/**
* Icon to display in the toast with error severity.
* @defaultValue pi pi-times
*/
errorIcon?: string | undefined;
/**
* Icon to display in the toast with success severity.
* @defaultValue pi pi-check
*/
successIcon?: string | undefined;
/**
* Uses to pass all properties of the HTMLButtonElement to the close button.
*/
closeButtonProps?: ButtonHTMLAttributes | undefined;
}
/**
* Defines valid slot in Toast component.
*/
export interface ToastSlots {
/**
* Custom message template.
* @param {Object} scope - message slot's params.
*/
message(scope: {
/**
* Message of the component
*/
message: any;
}): VNode[];
}
/**
* Defines valid emits in Toast component.
*/
export interface ToastEmits {
/**
* Callback to invoke when the toast is closed.
* @param {ToastMessageOptions} message - Toast message.
*/
close(message: ToastMessageOptions): void;
/**
* Callback to invoke when the toast's timeout is over.
* @param {ToastMessageOptions} message - Toast message.
*/
'life-end'(message: ToastMessageOptions): void;
}
/**
* **PrimeVue - Toast**
*
* _Toast is used to display messages in an overlay._
*
* [Live Demo](https://www.primevue.org/toast/)
* --- ---
* ![PrimeVue](https://primefaces.org/cdn/primevue/images/logo-100.png)
*
* @group Component
*
*/
declare class Toast extends ClassComponent<ToastProps, ToastSlots, ToastEmits> {}
declare module '@vue/runtime-core' {
interface GlobalComponents {
Toast: GlobalComponentConstructor<Toast>;
}
}
export default Toast;

View file

@ -0,0 +1,143 @@
import { mount } from '@vue/test-utils';
import PrimeVue from 'primevue/config';
import Toast from './Toast.vue';
describe('Toast.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Toast, {
global: {
plugins: [PrimeVue],
stubs: {
teleport: true,
transition: true
}
},
data() {
return {
messages: [{ severity: 'success', summary: 'Success Message', detail: 'Message Content', life: 3000 }]
};
}
});
});
it('should exist', () => {
expect(wrapper.find('.p-toast.p-component').exists()).toBe(true);
expect(wrapper.find('.p-toast-message').classes()).toContain('p-toast-message-success');
expect(wrapper.find('.p-toast-summary').text()).toBe('Success Message');
expect(wrapper.find('.p-toast-detail').text()).toBe('Message Content');
});
it('should position is changed', async () => {
await wrapper.setProps({ position: 'bottom-left' });
setTimeout(() => {
expect(wrapper.find('.p-toast.p-component').classes()).toContain('p-toast-bottom-left');
}, 200);
});
it('should show grouped toast', async () => {
await wrapper.setProps({ group: 'br' });
expect(wrapper.find('.p-toast.p-component').exists()).toBe(true);
});
it('should close toast', async () => {
await wrapper.vm.remove({ message: { severity: 'success', summary: 'Success Message', detail: 'Message Content', life: 3000 }, type: 'close' });
expect(wrapper.emitted()['close'][0][0].message).not.toBe({});
expect(wrapper.find('.p-toast-message').exists()).toBe(false);
});
it('should show multiple toast', async () => {
await wrapper.setData({
messages: [
{ severity: 'info', summary: 'Message 1', detail: 'Message 1 Content', life: 3000 },
{ severity: 'info', summary: 'Message 2', detail: 'Message 2 Content', life: 3000 },
{ severity: 'info', summary: 'Message 3', detail: 'Message 3 Content', life: 3000 }
]
});
expect(wrapper.findAll('.p-toast-message').length).toBe(3);
});
it('should close multiple toast', async () => {
await wrapper.setData({
messages: [
{ severity: 'info', summary: 'Message 1', detail: 'Message 1 Content', life: 3000 },
{ severity: 'info', summary: 'Message 2', detail: 'Message 2 Content', life: 3000 },
{ severity: 'info', summary: 'Message 3', detail: 'Message 3 Content', life: 3000 }
]
});
await wrapper.vm.onRemoveAllGroups();
expect(wrapper.findAll('.p-toast-message').length).toBe(0);
});
it('should close grouped toast', async () => {
await wrapper.setProps({ group: 'br' });
await wrapper.vm.onRemoveGroup('br');
expect(wrapper.findAll('.p-toast-message').length).toBe(0);
});
describe('custom icons', () => {
it('should have custom close icon', async () => {
await wrapper.setProps({ closeIcon: 'pi pi-discord' });
await wrapper.setData({
messages: [{ severity: 'info', summary: 'Message 1', detail: 'Message 1 Content', life: 3000 }]
});
const icon = wrapper.find('.p-toast-icon-close-icon');
expect(icon.classes()).toContain('pi-discord');
});
it('should have custom info severity icon', async () => {
await wrapper.setProps({ infoIcon: 'pi pi-discord' });
await wrapper.setData({
messages: [{ severity: 'info', summary: 'Message', detail: 'Message Content', life: 3000 }]
});
const icon = wrapper.find('.p-toast-message-icon');
expect(icon.classes()).toContain('pi-discord');
});
it('should have custom warn severity icon', async () => {
await wrapper.setProps({ warnIcon: 'pi pi-discord' });
await wrapper.setData({
messages: [{ severity: 'warn', summary: 'Message', detail: 'Message Content', life: 3000 }]
});
const icon = wrapper.find('.p-toast-message-icon');
expect(icon.classes()).toContain('pi-discord');
});
it('should have custom error severity icon', async () => {
await wrapper.setProps({ errorIcon: 'pi pi-discord' });
await wrapper.setData({
messages: [{ severity: 'error', summary: 'Message', detail: 'Message Content', life: 3000 }]
});
const icon = wrapper.find('.p-toast-message-icon');
expect(icon.classes()).toContain('pi-discord');
});
it('should have custom success severity icon', async () => {
await wrapper.setProps({ successIcon: 'pi pi-discord' });
await wrapper.setData({
messages: [{ severity: 'success', summary: 'Message', detail: 'Message Content', life: 3000 }]
});
const icon = wrapper.find('.p-toast-message-icon');
expect(icon.classes()).toContain('pi-discord');
});
});
});

304
components/lib/toast/Toast.vue Executable file
View file

@ -0,0 +1,304 @@
<template>
<Portal>
<div ref="container" :class="containerClass" v-bind="$attrs">
<transition-group name="p-toast-message" tag="div" @enter="onEnter" @leave="onLeave">
<ToastMessage
v-for="msg of messages"
:key="msg.id"
:message="msg"
:template="$slots.message"
:closeIcon="closeIcon"
:infoIcon="infoIcon"
:warnIcon="warnIcon"
:errorIcon="errorIcon"
:successIcon="successIcon"
:closeButtonProps="closeButtonProps"
@close="remove($event)"
/>
</transition-group>
</div>
</Portal>
</template>
<script>
import Portal from 'primevue/portal';
import ToastEventBus from 'primevue/toasteventbus';
import { ObjectUtils, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
import ToastMessage from './ToastMessage.vue';
var messageIdx = 0;
export default {
name: 'Toast',
inheritAttrs: false,
emits: ['close', 'life-end'],
props: {
group: {
type: String,
default: null
},
position: {
type: String,
default: 'top-right'
},
autoZIndex: {
type: Boolean,
default: true
},
baseZIndex: {
type: Number,
default: 0
},
breakpoints: {
type: Object,
default: null
},
closeIcon: {
type: String,
default: 'pi pi-times'
},
infoIcon: {
type: String,
default: 'pi pi-info-circle'
},
warnIcon: {
type: String,
default: 'pi pi-exclamation-triangle'
},
errorIcon: {
type: String,
default: 'pi pi-times'
},
successIcon: {
type: String,
default: 'pi pi-check'
},
closeButtonProps: {
type: null,
default: null
}
},
data() {
return {
messages: []
};
},
styleElement: null,
mounted() {
ToastEventBus.on('add', this.onAdd);
ToastEventBus.on('remove-group', this.onRemoveGroup);
ToastEventBus.on('remove-all-groups', this.onRemoveAllGroups);
if (this.breakpoints) {
this.createStyle();
}
},
beforeUnmount() {
this.destroyStyle();
if (this.$refs.container && this.autoZIndex) {
ZIndexUtils.clear(this.$refs.container);
}
ToastEventBus.off('add', this.onAdd);
ToastEventBus.off('remove-group', this.onRemoveGroup);
ToastEventBus.off('remove-all-groups', this.onRemoveAllGroups);
},
methods: {
add(message) {
if (message.id == null) {
message.id = messageIdx++;
}
this.messages = [...this.messages, message];
},
remove(params) {
let index = -1;
for (let i = 0; i < this.messages.length; i++) {
if (this.messages[i] === params.message) {
index = i;
break;
}
}
this.messages.splice(index, 1);
this.$emit(params.type, { message: params.message });
},
onAdd(message) {
if (this.group == message.group) {
this.add(message);
}
},
onRemoveGroup(group) {
if (this.group === group) {
this.messages = [];
}
},
onRemoveAllGroups() {
this.messages = [];
},
onEnter() {
this.$refs.container.setAttribute(this.attributeSelector, '');
if (this.autoZIndex) {
ZIndexUtils.set('modal', this.$refs.container, this.baseZIndex || this.$primevue.config.zIndex.modal);
}
},
onLeave() {
if (this.$refs.container && this.autoZIndex && ObjectUtils.isEmpty(this.messages)) {
setTimeout(() => {
ZIndexUtils.clear(this.$refs.container);
}, 200);
}
},
createStyle() {
if (!this.styleElement) {
this.styleElement = document.createElement('style');
this.styleElement.type = 'text/css';
document.head.appendChild(this.styleElement);
let innerHTML = '';
for (let breakpoint in this.breakpoints) {
let breakpointStyle = '';
for (let styleProp in this.breakpoints[breakpoint]) {
breakpointStyle += styleProp + ':' + this.breakpoints[breakpoint][styleProp] + '!important;';
}
innerHTML += `
@media screen and (max-width: ${breakpoint}) {
.p-toast[${this.attributeSelector}] {
${breakpointStyle}
}
}
`;
}
this.styleElement.innerHTML = innerHTML;
}
},
destroyStyle() {
if (this.styleElement) {
document.head.removeChild(this.styleElement);
this.styleElement = null;
}
}
},
computed: {
containerClass() {
return [
'p-toast p-component p-toast-' + this.position,
{
'p-input-filled': this.$primevue.config.inputStyle === 'filled',
'p-ripple-disabled': this.$primevue.config.ripple === false
}
];
},
attributeSelector() {
return UniqueComponentId();
}
},
components: {
ToastMessage: ToastMessage,
Portal: Portal
}
};
</script>
<style>
.p-toast {
position: fixed;
width: 25rem;
}
.p-toast-message-content {
display: flex;
align-items: flex-start;
}
.p-toast-message-text {
flex: 1 1 auto;
}
.p-toast-top-right {
top: 20px;
right: 20px;
}
.p-toast-top-left {
top: 20px;
left: 20px;
}
.p-toast-bottom-left {
bottom: 20px;
left: 20px;
}
.p-toast-bottom-right {
bottom: 20px;
right: 20px;
}
.p-toast-top-center {
top: 20px;
left: 50%;
transform: translateX(-50%);
}
.p-toast-bottom-center {
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.p-toast-center {
left: 50%;
top: 50%;
min-width: 20vw;
transform: translate(-50%, -50%);
}
.p-toast-icon-close {
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
.p-toast-icon-close.p-link {
cursor: pointer;
}
/* Animations */
.p-toast-message-enter-from {
opacity: 0;
-webkit-transform: translateY(50%);
-ms-transform: translateY(50%);
transform: translateY(50%);
}
.p-toast-message-leave-from {
max-height: 1000px;
}
.p-toast .p-toast-message.p-toast-message-leave-to {
max-height: 0;
opacity: 0;
margin-bottom: 0;
overflow: hidden;
}
.p-toast-message-enter-active {
-webkit-transition: transform 0.3s, opacity 0.3s;
transition: transform 0.3s, opacity 0.3s;
}
.p-toast-message-leave-active {
-webkit-transition: max-height 0.45s cubic-bezier(0, 1, 0, 1), opacity 0.3s, margin-bottom 0.3s;
transition: max-height 0.45s cubic-bezier(0, 1, 0, 1), opacity 0.3s, margin-bottom 0.3s;
}
</style>

View file

@ -0,0 +1,119 @@
<template>
<div :class="containerClass" role="alert" aria-live="assertive" aria-atomic="true">
<div class="p-toast-message-content" :class="message.contentStyleClass">
<template v-if="!template">
<span :class="iconClass"></span>
<div class="p-toast-message-text">
<span class="p-toast-summary">{{ message.summary }}</span>
<div class="p-toast-detail">{{ message.detail }}</div>
</div>
</template>
<component v-else :is="template" :message="message"></component>
<div v-if="message.closable !== false">
<button v-ripple class="p-toast-icon-close p-link" type="button" :aria-label="closeAriaLabel" @click="onCloseClick" autofocus v-bind="closeButtonProps">
<span :class="['p-toast-icon-close-icon', closeIcon]" />
</button>
</div>
</div>
</div>
</template>
<script>
import Ripple from 'primevue/ripple';
export default {
name: 'ToastMessage',
emits: ['close'],
props: {
message: {
type: null,
default: null
},
template: {
type: null,
default: null
},
closeIcon: {
type: String,
default: null
},
infoIcon: {
type: String,
default: null
},
warnIcon: {
type: String,
default: null
},
errorIcon: {
type: String,
default: null
},
successIcon: {
type: String,
default: null
},
closeButtonProps: {
type: null,
default: null
}
},
closeTimeout: null,
mounted() {
if (this.message.life) {
this.closeTimeout = setTimeout(() => {
this.close({ message: this.message, type: 'life-end' });
}, this.message.life);
}
},
beforeUnmount() {
this.clearCloseTimeout();
},
methods: {
close(params) {
this.$emit('close', params);
},
onCloseClick() {
this.clearCloseTimeout();
this.close({ message: this.message, type: 'close' });
},
clearCloseTimeout() {
if (this.closeTimeout) {
clearTimeout(this.closeTimeout);
this.closeTimeout = null;
}
}
},
computed: {
containerClass() {
return [
'p-toast-message',
this.message.styleClass,
{
'p-toast-message-info': this.message.severity === 'info',
'p-toast-message-warn': this.message.severity === 'warn',
'p-toast-message-error': this.message.severity === 'error',
'p-toast-message-success': this.message.severity === 'success'
}
];
},
iconClass() {
return [
'p-toast-message-icon',
{
[this.infoIcon]: this.message.severity === 'info',
[this.warnIcon]: this.message.severity === 'warn',
[this.errorIcon]: this.message.severity === 'error',
[this.successIcon]: this.message.severity === 'success'
}
];
},
closeAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.close : undefined;
}
},
directives: {
ripple: Ripple
}
};
</script>

View file

@ -0,0 +1,9 @@
{
"main": "./toast.cjs.js",
"module": "./toast.esm.js",
"unpkg": "./toast.min.js",
"types": "./Toast.d.ts",
"browser": {
"./sfc": "./Toast.vue"
}
}