Skip to content

Instantly share code, notes, and snippets.

@el0911
Created September 12, 2019 00:57
Show Gist options
  • Save el0911/5162d561cfd48afde9348f444e106edb to your computer and use it in GitHub Desktop.
Save el0911/5162d561cfd48afde9348f444e106edb to your computer and use it in GitHub Desktop.
import core from './core';
class State {
constructor() {
// all private varibale goes here
const state = {};
const event = {
change: []
};
// set base
state.tree = [{
id: 'root',
fields: []
}];
/**
* private function to traverse through each node and update the corresponding node
* @param {String} dropzoneID
* @param {String} parentID
* @param {Array} updatedFields - new fields/elements
* @param {Array} fields - fields/elements of current canvas
* @returns {Boolean}
*/
const traverseAndUpdateTree = (dropzoneID, parentID, updatedFields, fields = state.tree) => {
const matchedParentCanvas = fields.find(field => field.id === parentID);
let returnStatus = false;
// found element
if (matchedParentCanvas) {
if (dropzoneID === parentID) {
// first time, create fields
matchedParentCanvas.fields = updatedFields;
} else if (!matchedParentCanvas.fields || !updatedFields.length) {
// user tried to remove elements
if (dropzoneID && matchedParentCanvas.fields) {
matchedParentCanvas.fields = matchedParentCanvas.fields
.filter(f => f.dropzoneID !== dropzoneID);
} else {
// matched canvas does not has such field, create one
matchedParentCanvas.fields = updatedFields;
}
} else {
const fieldsToBeAdded = [];
// if user working on multi dropzone field
const matchedDropzoneFields = matchedParentCanvas.fields
.filter(f => f.dropzoneID === dropzoneID);
if (matchedDropzoneFields.length !== updatedFields.length) {
// some of the field got deleted from dropzone
// replace all fields with new fields
matchedParentCanvas.fields = updatedFields
.concat(matchedParentCanvas.fields
.filter(f => f.dropzoneID !== dropzoneID));
} else {
updatedFields.forEach((uField) => {
const fieldIndex = matchedParentCanvas.fields
.findIndex(f => f.id === uField.id);
// user try to add new field
if (fieldIndex === -1) {
fieldsToBeAdded.push(uField);
} else {
// user try to update existing field
matchedParentCanvas.fields = matchedParentCanvas.fields
.map((f, i) => (i === fieldIndex ? uField : f));
}
});
}
// add new field to existing array
if (fieldsToBeAdded.length) {
matchedParentCanvas.fields = matchedParentCanvas.fields.concat(fieldsToBeAdded);
}
}
returnStatus = true;
/* eslint no-else-return: 0 */
} else {
for (let i = 0; i < fields.length; i++) {
const childFields = fields[i].fields;
let status = false;
// field has sub-fields, check inside sub-fields
if (childFields) {
status = traverseAndUpdateTree(dropzoneID, parentID, updatedFields, childFields);
}
if (status) {
break;
}
}
}
return returnStatus;
};
// private function to trigger all change CB
const notifyStateChange = () => {
// trigger all events
event.change.forEach(e => e(state.tree));
};
// function to update state
// once update done, triggers CB and notifyStateChange
this.updateState = (dropzoneID, parentID, fields, cb = () => {}) => {
traverseAndUpdateTree(dropzoneID, parentID, fields);
cb(state.tree);
notifyStateChange();
};
/**
* function to return current state of tree
* @param {Object} - state tree
*/
this.getState = () => (state.tree);
this.delete = (id) =>{
const newState = this.deleteComponent(id)
// state.tree = newState
notifyStateChange()
}
this.deleteComponent = ( id,arr = state.tree,parent )=>{
for (let i = 0; i < arr.length; i++) {
const element = arr[i];
if ( element && id == element.id) {
delete arr[i]
return true
}
else if (element && element.fields){
this.deleteComponent(id, element.fields,element)
}
}
return arr
}
/**
* function to clear the state
* loop though all parent(end level) nodes, and call `flushDroppedElements` function
* so that component as well as application state gets flushed
*/
this.clearState = (cb = () => {}) => {
const rootNode = state.tree[0];
const topLevelFields = rootNode.fields.length;
// canvas is empty, just notify other
if (!rootNode.fields.length) {
notifyStateChange();
cb();
}
// rootNode.fields.forEach((topLevelElement, i) => {
// topLevelElement.flushDroppedElements(() => {
// if (i === topLevelFields - 1) {
// notifyStateChange();
// cb();
// }
// });
// });
for (let i = 0; i < rootNode.length; i++) {
const topLevelElement = rootNode[i];
topLevelElement.flushDroppedElements(() => {
if (i === topLevelFields - 1) {
notifyStateChange();
cb();
}
});
}
return true;
};
/**
* function to add event
* @param {String} eventName
* @param {function} cb - callback
*/
this.addEventListener = (eventName, cb) => {
let returnCB = null;
if (typeof cb !== 'function') {
core.error('`cb` param has to be function');
return false;
}
if (Object.prototype.hasOwnProperty.call(event, eventName)) {
event[eventName].push(cb);
returnCB = cb;
} else {
core.error('No such event');
}
return returnCB;
};
/**
* function to remove event
* @param {String} eventName
* @param {function} cb - callback
*/
this.removeEventListener = (eventName, cb) => {
if (Object.prototype.hasOwnProperty.call(event, eventName)) {
event[eventName] = event[eventName].filter(e => e !== cb);
} else {
core.error('No such event');
}
};
}
}
const state = new State();
export default state;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment