PropertyTable 생성
This commit is contained in:
@@ -1,90 +1,59 @@
|
||||
class EditableInput {
|
||||
constructor(options = {}) {
|
||||
this.value = options.value || '';
|
||||
this.name = options.name || '';
|
||||
this.id = options.id || '';
|
||||
this.class = options.class || 'form-control';
|
||||
this.placeholder = options.placeholder || '';
|
||||
this.label = options.label || '';
|
||||
class EditableInputModel {
|
||||
constructor(value = '') {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
init(element, callback) {
|
||||
this.element = element;
|
||||
this.element.innerHTML = this.render();
|
||||
this.attachEventListeners(callback);
|
||||
setValue(newValue) {
|
||||
this.value = newValue;
|
||||
}
|
||||
|
||||
val(newValue) {
|
||||
if (newValue !== undefined) {
|
||||
this.value = newValue;
|
||||
this.updateUI();
|
||||
}
|
||||
getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
const editableDiv = this.element.querySelector('.editableInputContent');
|
||||
const hiddenInput = this.element.querySelector('input[type="hidden"]');
|
||||
|
||||
if (this.value) {
|
||||
editableDiv.innerHTML = this.renderContent(this.parseContent(this.value));
|
||||
} else {
|
||||
editableDiv.innerHTML = '​'; // Insert a zero-width space
|
||||
}
|
||||
hiddenInput.value = this.value;
|
||||
class EditableInputView {
|
||||
constructor(model, editableInput) {
|
||||
this.model = model;
|
||||
this.editableInput = editableInput;
|
||||
}
|
||||
|
||||
render() {
|
||||
let contentHtml = this.value ? this.renderContent(this.parseContent(this.value)) : '​';
|
||||
render(element) {
|
||||
this.element = element;
|
||||
this.element.innerHTML = this.generateInputHtml();
|
||||
this.editableDiv = this.element.querySelector('.editableInputContent');
|
||||
this.attachEventListeners();
|
||||
}
|
||||
|
||||
generateInputHtml() {
|
||||
let contentHtml = this.model.getValue() ? this.renderContent(this.parseContent(this.model.getValue())) : '​';
|
||||
return `
|
||||
<div contenteditable="true" class="editableInputContent ${this.class}" style="border-bottom-left-radius: 0; border-top-left-radius: 0;" placeholder="${this.placeholder}">
|
||||
${contentHtml}
|
||||
</div>
|
||||
<input type="hidden" name="${this.name}" value="${this.value}" />
|
||||
<label>${this.label}</label>
|
||||
`;
|
||||
<div contenteditable="true" class="editableInputContent form-control" data-value="${this.model.getValue()}">
|
||||
${contentHtml}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
attachEventListeners(callback) {
|
||||
const editableDiv = this.element.querySelector('.editableInputContent');
|
||||
const hiddenInput = this.element.querySelector('input[type="hidden"]');
|
||||
|
||||
editableDiv.addEventListener('input', (event) => {
|
||||
this.handleInput(event, editableDiv, hiddenInput);
|
||||
if (callback)
|
||||
callback(event);
|
||||
attachEventListeners() {
|
||||
this.editableDiv.addEventListener('input', (event) => {
|
||||
this.editableInput.controller.handleInput(this.editableDiv);
|
||||
if (this.editableInput.controller.additionalCallback) {
|
||||
this.editableInput.controller.additionalCallback(this.model.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleInput(event, editableDiv, hiddenInput) {
|
||||
|
||||
let cursor = this.getCurrentCursor(editableDiv);
|
||||
let mergedString = Array.from(editableDiv.childNodes)
|
||||
.filter(function(node) {
|
||||
// Keep only SPAN elements and text nodes
|
||||
return node.nodeName.toUpperCase() === 'SPAN' || node.nodeType === 3;
|
||||
})
|
||||
.map(node => {
|
||||
return node.nodeType === 3 ? node.data.trim() : node.innerText.trim();
|
||||
})
|
||||
.join('');
|
||||
|
||||
let content = this.parseContent(mergedString);
|
||||
this.value = content.join('');
|
||||
hiddenInput.value = this.value;
|
||||
|
||||
if (!this.value) {
|
||||
editableDiv.innerHTML = '​'; // Insert a zero-width space
|
||||
cursor = 0;
|
||||
updateUI() {
|
||||
let content = this.model.getValue();
|
||||
if (content) {
|
||||
this.editableDiv.innerHTML = this.renderContent(this.parseContent(content));
|
||||
} else {
|
||||
editableDiv.innerHTML = this.renderContent(content);
|
||||
this.editableDiv.innerHTML = '​'; // Insert a zero-width space
|
||||
}
|
||||
|
||||
this.restoreCursor(editableDiv, cursor);
|
||||
this.editableDiv.setAttribute('data-value', content);
|
||||
}
|
||||
|
||||
|
||||
renderContent(contentArray) {
|
||||
let content = '';
|
||||
contentArray.forEach((item) => {
|
||||
@@ -122,6 +91,20 @@ class EditableInput {
|
||||
|
||||
return returnStringArray;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class EditableInputController {
|
||||
constructor(model, view) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
init(element, callback) {
|
||||
this.view.render(element);
|
||||
this.additionalCallback = callback;
|
||||
}
|
||||
|
||||
|
||||
getCurrentCursor(element) {
|
||||
const selection = window.getSelection();
|
||||
@@ -134,6 +117,53 @@ class EditableInput {
|
||||
return preCaretRange.toString().trim().length;
|
||||
}
|
||||
|
||||
handleInput(editableDiv) {
|
||||
|
||||
let cursor = this.getCurrentCursor(editableDiv);
|
||||
let mergedString = Array.from(editableDiv.childNodes)
|
||||
.filter(function (node) {
|
||||
// Keep only SPAN elements and text nodes
|
||||
return node.nodeName.toUpperCase() === 'SPAN' || node.nodeType === 3;
|
||||
})
|
||||
.map(node => {
|
||||
return node.nodeType === 3 ? node.data.trim() : node.innerText.trim();
|
||||
})
|
||||
.join('');
|
||||
|
||||
let content = this.parseContent(mergedString);
|
||||
this.model.setValue(content.join(''));
|
||||
// Optionally, update the view if needed
|
||||
this.view.updateUI();
|
||||
|
||||
this.restoreCursor(editableDiv, cursor);
|
||||
}
|
||||
|
||||
parseContent(inputString) {
|
||||
let returnStringArray = [];
|
||||
let regex = /{{.*?}}/g;
|
||||
let lastIndex = 0;
|
||||
|
||||
inputString.replace(regex, (match, index) => {
|
||||
// Add the string before the match
|
||||
if (index > lastIndex) {
|
||||
returnStringArray.push(inputString.slice(lastIndex, index));
|
||||
}
|
||||
|
||||
// Add the matched string including the delimiters
|
||||
returnStringArray.push(match);
|
||||
|
||||
// Update lastIndex for the next iteration
|
||||
lastIndex = index + match.length;
|
||||
});
|
||||
|
||||
// Add any remaining string after the last match
|
||||
if (lastIndex < inputString.length) {
|
||||
returnStringArray.push(inputString.slice(lastIndex));
|
||||
}
|
||||
|
||||
return returnStringArray;
|
||||
}
|
||||
|
||||
restoreCursor(element, position) {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
@@ -167,7 +197,42 @@ class EditableInput {
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
class EditableInput {
|
||||
constructor(options = {}) {
|
||||
// Initialize the model with the value
|
||||
this.model = new EditableInputModel(options.value || '');
|
||||
|
||||
// Create the view and controller, passing the model to them
|
||||
this.view = new EditableInputView(this.model, this);
|
||||
this.controller = new EditableInputController(this.model, this.view);
|
||||
|
||||
// Additional properties as required
|
||||
this.name = options.name || '';
|
||||
this.id = options.id || '';
|
||||
this.class = options.class || 'form-control';
|
||||
this.placeholder = options.placeholder || '';
|
||||
}
|
||||
|
||||
init(element, callback) {
|
||||
// Initialize the controller with the DOM element
|
||||
this.controller.init(element, callback);
|
||||
}
|
||||
|
||||
getValue() {
|
||||
// Delegate to the model
|
||||
return this.model.getValue();
|
||||
}
|
||||
|
||||
setValue(newValue) {
|
||||
// Delegate to the model and update the view
|
||||
this.model.setValue(newValue);
|
||||
this.view.updateUI();
|
||||
}
|
||||
|
||||
// Any additional methods required for interaction with this component
|
||||
}
|
||||
|
||||
export default EditableInput;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user