1import { h } from 'vue'; 2import StatusIcon from '../Global/StatusIcon'; 3import i18n from '@/i18n'; 4const BVToastMixin = { 5 components: { 6 StatusIcon, 7 }, 8 methods: { 9 $_BVToastMixin_createTitle(title, status) { 10 const statusIcon = h(StatusIcon, { status }); 11 return h('strong', { class: 'toast-icon' }, [statusIcon, title]); 12 }, 13 $_BVToastMixin_createBody(messageBody) { 14 if (Array.isArray(messageBody)) { 15 return messageBody.map((message) => h('p', { class: 'mb-0' }, message)); 16 } else { 17 return [h('p', { class: 'mb-0' }, messageBody)]; 18 } 19 }, 20 $_BVToastMixin_createTimestamp() { 21 const timestamp = this.$filters.formatTime(new Date()); 22 return h('p', { class: 'mt-3 mb-0' }, timestamp); 23 }, 24 $_BVToastMixin_createRefreshAction() { 25 return h( 26 'BLink', 27 { 28 class: 'd-inline-block mt-3', 29 onClick: () => { 30 require('@/eventBus').default.$emit('refresh-application'); 31 }, 32 }, 33 i18n.global.t('global.action.refresh'), 34 ); 35 }, 36 $_BVToastMixin_initToast(body, title, variant) { 37 // Use global toast plugin (works with Options API) 38 // Extract text content from VNodes for display 39 40 // Extract title text from VNode 41 const titleText = 42 typeof title === 'string' 43 ? title 44 : title?.children?.[1] || title?.children || ''; 45 46 // Extract body text from VNode array 47 // Each VNode (paragraph) should be on its own line 48 const bodyLines = Array.isArray(body) 49 ? body.map((node) => { 50 if (typeof node === 'string') return node; 51 // Extract text from VNode children 52 const text = node?.children || node?.props?.children || ''; 53 // Ensure timestamps and other paragraphs are on separate lines 54 return text; 55 }) 56 : [typeof body === 'string' ? body : body?.children || '']; 57 58 // Join with newlines to ensure timestamps appear on their own line 59 const bodyText = bodyLines.filter(Boolean).join('\n'); 60 61 // Show toast via global plugin 62 if (this.$toast) { 63 this.$toast.show({ 64 body: bodyText, 65 props: { 66 title: titleText, 67 variant, 68 isStatus: true, 69 solid: false, // Use light backgrounds with dark text (not solid colors) 70 // Success toasts auto-dismiss after 10s, others stay until closed 71 interval: variant === 'success' ? 10000 : 0, 72 // Note: Progress bar hidden via CSS in _toasts.scss (JS props to hide progress bar don't work as documented in Bootstrap Vue Next 0.40.8) 73 }, 74 }); 75 } else { 76 // Fallback: log to console 77 /* eslint-disable no-console */ 78 console[variant === 'danger' ? 'error' : 'log']( 79 `[toast:${variant}]`, 80 bodyText, 81 ); 82 /* eslint-enable no-console */ 83 } 84 }, 85 successToast( 86 message, 87 { 88 title: t = i18n.global.t('global.status.success'), 89 timestamp, 90 refreshAction, 91 } = {}, 92 ) { 93 const body = this.$_BVToastMixin_createBody(message); 94 const title = this.$_BVToastMixin_createTitle(t, 'success'); 95 if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction()); 96 if (timestamp) { 97 body.push(' '); // Extra newline for spacing above timestamp 98 body.push(this.$_BVToastMixin_createTimestamp()); 99 } 100 this.$_BVToastMixin_initToast(body, title, 'success'); 101 }, 102 errorToast( 103 message, 104 { 105 title: t = i18n.global.t('global.status.error'), 106 timestamp, 107 refreshAction, 108 } = {}, 109 ) { 110 const body = this.$_BVToastMixin_createBody(message); 111 const title = this.$_BVToastMixin_createTitle(t, 'danger'); 112 if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction()); 113 if (timestamp) { 114 body.push(' '); // Extra newline for spacing above timestamp 115 body.push(this.$_BVToastMixin_createTimestamp()); 116 } 117 this.$_BVToastMixin_initToast(body, title, 'danger'); 118 }, 119 warningToast( 120 message, 121 { 122 title: t = i18n.global.t('global.status.warning'), 123 timestamp, 124 refreshAction, 125 } = {}, 126 ) { 127 const body = this.$_BVToastMixin_createBody(message); 128 const title = this.$_BVToastMixin_createTitle(t, 'warning'); 129 if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction()); 130 if (timestamp) { 131 body.push(' '); // Extra newline for spacing above timestamp 132 body.push(this.$_BVToastMixin_createTimestamp()); 133 } 134 this.$_BVToastMixin_initToast(body, title, 'warning'); 135 }, 136 infoToast( 137 message, 138 { 139 title: t = i18n.global.t('global.status.informational'), 140 timestamp, 141 refreshAction, 142 } = {}, 143 ) { 144 const body = this.$_BVToastMixin_createBody(message); 145 const title = this.$_BVToastMixin_createTitle(t, 'info'); 146 if (refreshAction) body.push(this.$_BVToastMixin_createRefreshAction()); 147 if (timestamp) { 148 body.push(' '); // Extra newline for spacing above timestamp 149 body.push(this.$_BVToastMixin_createTimestamp()); 150 } 151 this.$_BVToastMixin_initToast(body, title, 'info'); 152 }, 153 }, 154}; 155 156export default BVToastMixin; 157