Files
sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/Edit Page - SIS Vietnam - Manage_files/timeline.js.download
T

339 lines
15 KiB
Plaintext

class SISTimelineTool {
static get toolbox() {
return {
title: 'Timeline Group',
icon: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>'
};
}
constructor({ data, api, readOnly }) {
this.api = api;
this.readOnly = readOnly;
this.data = {
globalAttributes: (data && data.globalAttributes) || (data && data.attributes) || '',
items: (data && data.items && Array.isArray(data.items)) ? data.items : []
};
// Add an initial empty item if list is empty
if (this.data.items.length === 0) {
this.data.items.push({ date: '', description: '', imageUrl: '' });
}
this.wrapper = undefined;
}
render() {
this.wrapper = document.createElement('div');
this.wrapper.className = 'timeline-tool-wrapper';
this.wrapper.style.border = '1px solid #e3e6f0';
this.wrapper.style.borderRadius = '8px';
this.wrapper.style.padding = '14px';
this.wrapper.style.background = '#f8f9fc';
this.wrapper.style.marginBottom = '12px';
const headerLabel = document.createElement('label');
headerLabel.className = 'font-weight-bold text-primary small mb-2 d-block';
headerLabel.innerHTML = '<i class="fas fa-stream"></i> Timeline Group Settings';
this.wrapper.appendChild(headerLabel);
const globalAttrGroup = this._createInput('e.g. data-aos="fade-up" style="..."', this.data.globalAttributes, 'global-attr', '<i class="fas fa-sliders-h"></i> Global Custom Attributes');
this.wrapper.appendChild(globalAttrGroup);
this.itemsContainer = document.createElement('div');
this.wrapper.appendChild(this.itemsContainer);
this.data.items.forEach((item, index) => {
this._renderItem(item, index);
});
if (!this.readOnly) {
const addButton = document.createElement('button');
addButton.type = 'button';
addButton.className = 'btn btn-sm btn-outline-primary mt-2';
addButton.innerHTML = '<i class="fas fa-plus"></i> Add Event';
addButton.addEventListener('click', () => {
const newItem = { date: '', description: '', imageUrl: '' };
this.data.items.push(newItem);
this._renderItem(newItem, this.data.items.length - 1);
});
this.wrapper.appendChild(addButton);
}
return this.wrapper;
}
_renderItem(item, index) {
const itemBox = document.createElement('div');
itemBox.className = 'timeline-item-edit-box mb-3 p-3 bg-white border rounded';
itemBox.style.position = 'relative';
if (!this.readOnly) {
const removeBtn = document.createElement('button');
removeBtn.type = 'button';
removeBtn.className = 'btn btn-sm btn-danger';
removeBtn.style.position = 'absolute';
removeBtn.style.top = '10px';
removeBtn.style.right = '10px';
removeBtn.innerHTML = '<i class="fas fa-times"></i>';
removeBtn.addEventListener('click', () => {
itemBox.remove();
});
itemBox.appendChild(removeBtn);
}
const dateGroup = this._createInput('Date (e.g. 10/04/1994)', item.date, 'date', '<i class="fas fa-calendar-alt"></i> Date / Milestone');
const descGroup = this._createTextarea('Description (e.g. Bệnh viện khai trương)', item.description, 'desc', '<i class="fas fa-align-left"></i> Description');
// Image input with preview, upload, and media library buttons
const imageGroup = document.createElement('div');
imageGroup.className = 'form-group mb-2';
const imageLabel = document.createElement('label');
imageLabel.className = 'small font-weight-bold text-secondary mb-1 d-block';
imageLabel.innerHTML = '<i class="fas fa-image"></i> Image URL (Optional)';
const inputRow = document.createElement('div');
inputRow.className = 'input-group input-group-sm mb-2';
const imageInput = document.createElement('input');
imageInput.type = 'text';
imageInput.className = 'form-control ce-timeline-input-image';
imageInput.placeholder = 'Paste image URL here...';
imageInput.value = item.imageUrl || '';
if (this.readOnly) imageInput.disabled = true;
const appendDiv = document.createElement('div');
appendDiv.className = 'input-group-append';
const chooseBtn = document.createElement('button');
chooseBtn.type = 'button';
chooseBtn.className = 'btn btn-info';
chooseBtn.innerHTML = '<i class="fas fa-folder-open"></i> Choose';
if (this.readOnly) chooseBtn.disabled = true;
const uploadBtn = document.createElement('button');
uploadBtn.type = 'button';
uploadBtn.className = 'btn btn-primary';
uploadBtn.innerHTML = '<i class="fas fa-upload"></i> Upload';
if (this.readOnly) uploadBtn.disabled = true;
// Hidden file input for uploading
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = 'image/*';
fileInput.style.display = 'none';
chooseBtn.addEventListener('click', () => {
if (window.SISMediaPicker) {
SISMediaPicker.open((url, mediaObj, config) => {
imageInput.value = url;
// Store config in hidden inputs on the item box
let styleInp = itemBox.querySelector('.ce-timeline-input-imgstyle');
if (!styleInp) {
styleInp = document.createElement('input'); styleInp.type = 'hidden';
styleInp.className = 'ce-timeline-input-imgstyle'; itemBox.appendChild(styleInp);
}
let classInp = itemBox.querySelector('.ce-timeline-input-imgclass');
if (!classInp) {
classInp = document.createElement('input'); classInp.type = 'hidden';
classInp.className = 'ce-timeline-input-imgclass'; itemBox.appendChild(classInp);
}
let altInp = itemBox.querySelector('.ce-timeline-input-imgalt');
if (!altInp) {
altInp = document.createElement('input'); altInp.type = 'hidden';
altInp.className = 'ce-timeline-input-imgalt'; itemBox.appendChild(altInp);
}
let attrsInp = itemBox.querySelector('.ce-timeline-input-imgattrs');
if (!attrsInp) {
attrsInp = document.createElement('input'); attrsInp.type = 'hidden';
attrsInp.className = 'ce-timeline-input-imgattrs'; itemBox.appendChild(attrsInp);
}
let arInp = itemBox.querySelector('.ce-timeline-input-imgar');
if (!arInp) {
arInp = document.createElement('input'); arInp.type = 'hidden';
arInp.className = 'ce-timeline-input-imgar'; itemBox.appendChild(arInp);
}
if (config) {
styleInp.value = config.style || '';
classInp.value = config.cssClass || '';
altInp.value = config.alt || '';
attrsInp.value = config.customAttributes || '';
arInp.value = config.aspectRatio || '';
}
updatePreview();
});
} else {
alert('SISMediaPicker is not loaded.');
}
});
uploadBtn.addEventListener('click', () => {
fileInput.click();
});
const updatePreview = () => {
const url = imageInput.value.trim();
if (url) {
imagePreview.style.backgroundImage = 'url("' + url + '")';
imagePreview.style.display = 'block';
} else {
imagePreview.style.backgroundImage = 'none';
imagePreview.style.display = 'none';
}
};
fileInput.addEventListener('change', (e) => {
if (e.target.files && e.target.files[0]) {
const formData = new FormData();
formData.append('file', e.target.files[0]);
uploadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
uploadBtn.disabled = true;
fetch('/api/manage/media/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success === 1 && data.file && data.file.url) {
imageInput.value = data.file.url;
updatePreview();
} else {
alert('Upload failed: ' + (data.message || 'Unknown error'));
}
})
.catch(err => {
console.error('Error uploading image', err);
alert('Upload failed: ' + err.message);
})
.finally(() => {
uploadBtn.innerHTML = '<i class="fas fa-upload"></i> Upload';
uploadBtn.disabled = false;
fileInput.value = ''; // Reset
});
}
});
appendDiv.appendChild(chooseBtn);
appendDiv.appendChild(uploadBtn);
inputRow.appendChild(imageInput);
inputRow.appendChild(appendDiv);
inputRow.appendChild(fileInput);
// Image Preview Container
const imagePreview = document.createElement('div');
imagePreview.style.width = '100%';
imagePreview.style.height = '180px';
imagePreview.style.backgroundColor = '#f8f9fc';
imagePreview.style.border = '1px dashed #d1d3e2';
imagePreview.style.borderRadius = '6px';
imagePreview.style.backgroundSize = 'cover';
imagePreview.style.backgroundPosition = 'center';
imagePreview.style.display = 'none';
imageInput.addEventListener('input', updatePreview);
updatePreview();
imageGroup.appendChild(imageLabel);
imageGroup.appendChild(inputRow);
imageGroup.appendChild(imagePreview);
const attrGroup = this._createInput('e.g. data-aos="fade-right" style="..."', item.attributes, 'attr', '<i class="fas fa-sliders-h"></i> Step Custom Attributes');
itemBox.appendChild(dateGroup);
itemBox.appendChild(descGroup);
itemBox.appendChild(imageGroup);
itemBox.appendChild(attrGroup);
this.itemsContainer.appendChild(itemBox);
}
_createInput(placeholder, value, type, labelHtml) {
const wrapper = document.createElement('div');
wrapper.className = 'form-group mb-2';
if (labelHtml) {
const label = document.createElement('label');
label.className = 'small font-weight-bold text-secondary mb-1 d-block';
label.innerHTML = labelHtml;
wrapper.appendChild(label);
}
const inp = document.createElement('input');
inp.type = 'text';
inp.className = 'form-control form-control-sm ce-timeline-input-' + type;
inp.placeholder = placeholder;
inp.value = value || '';
if (this.readOnly) inp.disabled = true;
wrapper.appendChild(inp);
return wrapper;
}
_createTextarea(placeholder, value, type, labelHtml) {
const wrapper = document.createElement('div');
wrapper.className = 'form-group mb-2';
if (labelHtml) {
const label = document.createElement('label');
label.className = 'small font-weight-bold text-secondary mb-1 d-block';
label.innerHTML = labelHtml;
wrapper.appendChild(label);
}
const inp = document.createElement('textarea');
inp.className = 'form-control form-control-sm ce-timeline-input-' + type;
inp.placeholder = placeholder;
inp.value = value || '';
inp.style.minHeight = '70px';
if (this.readOnly) inp.disabled = true;
wrapper.appendChild(inp);
return wrapper;
}
save(blockContent) {
const itemBoxes = this.itemsContainer.querySelectorAll('.timeline-item-edit-box');
const items = [];
itemBoxes.forEach(box => {
const date = box.querySelector('.ce-timeline-input-date').value.trim();
const desc = box.querySelector('.ce-timeline-input-desc').value.trim();
const img = box.querySelector('.ce-timeline-input-image').value.trim();
const attrEl = box.querySelector('.ce-timeline-input-attr');
const attr = attrEl ? attrEl.value.trim() : '';
const imgStyle = (box.querySelector('.ce-timeline-input-imgstyle') || {}).value || '';
const imgClass = (box.querySelector('.ce-timeline-input-imgclass') || {}).value || '';
const imgAlt = (box.querySelector('.ce-timeline-input-imgalt') || {}).value || '';
const imgAttrs = (box.querySelector('.ce-timeline-input-imgattrs') || {}).value || '';
const imgAspectRatio = (box.querySelector('.ce-timeline-input-imgar') || {}).value || '';
if (date || desc || img || attr) {
items.push({
date: date, description: desc, imageUrl: img, attributes: attr,
imageStyle: imgStyle, imageClass: imgClass, imageAlt: imgAlt,
imageAttrs: imgAttrs, imageAspectRatio: imgAspectRatio
});
}
});
const globalAttrEl = this.wrapper.querySelector('.ce-timeline-input-global-attr');
return {
globalAttributes: globalAttrEl ? globalAttrEl.value.trim() : (this.data.globalAttributes || ''),
items: items
};
}
}
// Register the plugin globally
window.SISEditorPlugins = window.SISEditorPlugins || {};
window.SISEditorPlugins['timeline'] = {
class: SISTimelineTool
};
// openMediaPickerModal — legacy stub, replaced by SISMediaPicker
function openMediaPickerModal(onSelectCallback) {
if (window.SISMediaPicker) {
SISMediaPicker.open(function(url) { onSelectCallback(url); });
} else {
var url = prompt('Enter image URL:');
if (url) onSelectCallback(url);
}
}