besper-frontend-site-dev-main
Version:
Professional B-esper Frontend Site - Site-wide integration toolkit for full website bot deployment
208 lines (186 loc) • 5.75 kB
JavaScript
/**
* Message Component
* Handles individual message rendering and behavior in the chat widget
*/
export class Message {
constructor(content, sender, options = {}) {
this.content = content;
this.sender = sender; // 'user' or 'bot'
this.timestamp = options.timestamp || new Date().toISOString();
this.id = options.id || this.generateMessageId();
this.config = options.config || {};
}
/**
* Generate a unique message ID
* @returns {string} Unique message ID
*/
generateMessageId() {
return 'msg-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
}
/**
* Get the formatted message time
* @returns {string} Formatted time string
*/
getFormattedTime() {
const messageDate = this.timestamp ? new Date(this.timestamp) : new Date();
return messageDate.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
});
}
/**
* Get the avatar HTML for the message
* @returns {string} Avatar HTML
*/
getAvatarHTML() {
const config = this.config || {};
if (this.sender === 'bot') {
const shouldHideAvatar = !config.logoUrl;
const style = shouldHideAvatar ? 'style="display: none;"' : '';
return `
<div class="besper-message-avatar besper-${this.sender}" ${style}>
${
config.logoUrl
? `<img src="${config.logoUrl}" alt="Bot Logo" class="besper-avatar-image">`
: ''
}
</div>
`;
} else {
return `
<div class="besper-message-avatar besper-${this.sender}">
U
</div>
`;
}
}
/**
* Get the overflow warning HTML if needed
* @returns {string} Overflow warning HTML or empty string
*/
getOverflowWarningHTML() {
// Only show overflow warning for bot messages with tables
if (this.sender !== 'bot' || !this.hasTable()) {
return '';
}
return `
<div class="besper-table-overflow-warning" style="display: none;">
<div class="besper-overflow-content">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>
</svg>
<span class="besper-overflow-text">Optimize view for better readability (Click to expand)</span>
</div>
</div>
`;
}
/**
* Check if the message content contains tables
* @returns {boolean} Whether the message has tables
*/
hasTable() {
return typeof this.content === 'string' && this.content.includes('<table');
}
/**
* Generate the complete HTML for the message
* @returns {string} Message HTML string
*/
getHTML() {
const messageTime = this.getFormattedTime();
const avatarHTML = this.getAvatarHTML();
const overflowWarningHTML = this.getOverflowWarningHTML();
return `
<div class="besper-message besper-${this.sender}" id="${this.id}">
${avatarHTML}
<div class="besper-message-content">
<div class="besper-message-text">${this.content}</div>
${overflowWarningHTML}
<div class="besper-message-time">${messageTime}</div>
</div>
</div>
`;
}
/**
* Check for table overflow after the message is rendered
* @param {HTMLElement} messageElement - The rendered message element
* @param {Function} onOverflow - Callback function when overflow is detected
*/
checkTableOverflow(messageElement, onOverflow) {
if (!messageElement || this.sender !== 'bot') return;
// Skip overflow check on mobile devices
if (this.isMobileDevice()) return;
const tables = messageElement.querySelectorAll('table');
if (tables.length === 0) return;
let hasOverflow = false;
tables.forEach(table => {
if (table.scrollWidth > table.clientWidth) {
hasOverflow = true;
}
});
if (hasOverflow) {
const warningElement = messageElement.querySelector(
'.besper-table-overflow-warning'
);
if (warningElement) {
warningElement.style.display = 'flex';
if (onOverflow) {
onOverflow(warningElement);
}
}
}
}
/**
* Setup event listeners for the message
* @param {HTMLElement} messageElement - The rendered message element
* @param {Object} callbacks - Callback functions for events
*/
setupEventListeners(messageElement, callbacks = {}) {
// Table overflow warning click handler
const overflowWarning = messageElement.querySelector(
'.besper-table-overflow-warning'
);
if (overflowWarning && callbacks.onOverflowClick) {
overflowWarning.addEventListener('click', () => {
callbacks.onOverflowClick();
});
}
// Any other message-specific interactions can be added here
}
/**
* Mobile device detection
* @returns {boolean} Whether the device is mobile
*/
isMobileDevice() {
return (
window.innerWidth <= 480 ||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
);
}
/**
* Convert the message to a plain object for storage
* @returns {Object} Message data object
*/
toObject() {
return {
id: this.id,
content: this.content,
sender: this.sender,
timestamp: this.timestamp,
};
}
/**
* Create a Message instance from a plain object
* @param {Object} data - Message data object
* @param {Object} config - Bot configuration
* @returns {Message} Message instance
*/
static fromObject(data, config = {}) {
return new Message(data.content, data.sender, {
id: data.id,
timestamp: data.timestamp,
config,
});
}
}