Created
March 5, 2019 13:52
-
-
Save thedanbob/b48a9e20b57341ced21fdd9e2e0609b9 to your computer and use it in GitHub Desktop.
Cocoon rewrite in ES6 (WIP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Cocoon { | |
constructor(container, options) { | |
this.container = this.getNodeFromOption(container, false) | |
if (!this.container) { | |
throw new TypeError('Container must be supplied') | |
} | |
this.addFieldsLink = this.getNodeFromOption(options.addFieldsLink, container.querySelector('.add_fields')) | |
if (!this.addFieldsLink) | |
console.warn("Couldn't find the link to add fields. Make sure your `link_to_add_association` is correct.") | |
this.insertionNode = this.getNodeFromOption(options.insertionNode || this.addFieldsLink.getAttribute('data-association-insertion-node'), this.addFieldsLink.parentNode) | |
if (!this.insertionNode) | |
console.warn("Couldn't find the element to insert the template. Make sure your `insertionNode` option is correct.") | |
this.insertionFunc = option.insertionFunc || function(refNode, content) { | |
refNode.insertAdjacentHTML('beforebegin', content) | |
return refNode.previousSibling | |
} | |
this.beforeInsert = option.beforeInsert | |
this.afterInsert = option.afterInsert | |
this.beforeRemove = option.beforeRemove | |
this.afterRemove = option.afterRemove | |
this.elementCount = 0 | |
document.addEventListener('click', (e) => { | |
if (e.target.classList.includes('add_fields')) | |
this.addFields(e) | |
else if (e.target.classList.includes('remove_fields')) | |
this.removeFields(e) | |
}) | |
var removeFunc = function() { | |
[...document.querySelectorAll('.remove_fields.existing.destroyed')].forEach(function(el) { | |
var wrapperClass = el.getAttribute('data-wrapper-class') || 'nested-fields' | |
var removed = el.parentNode | |
while (!removed.classList.includes(wrapperClass)) { | |
removed = removed.parentNode | |
} | |
removed.style.display = 'none' | |
}) | |
} | |
document.addEventListener('DOMContentLoaded', removeFunc) | |
document.addEventListener('turbolinks:load', removeFund) | |
} | |
getNodeFromOption(option, defaultOpt) { | |
if (!option) return defaultOpt | |
if (typeof option == 'string') | |
return document.querySelector(option) | |
else | |
return option | |
} | |
createNewId() { | |
return new Date().getTime() + this.elementCount++ | |
} | |
newIdBraced(id) { | |
return `[${id}]$1` | |
} | |
newIdUnderscored(id) { | |
return `_${id}_$1` | |
} | |
addFields(e) { | |
e.preventDefault() | |
var link = e.target, | |
assoc = link.getAttribute('data-association'), | |
assocs = link.getAttribute('data-associations'), | |
content = link.getAttribute('data-association-insertion-template'), | |
count = parseInt(link.getAttribute('data-count'), 10), | |
regexpBraced = new RegExp(`\\[new_${assoc}\\](.*?\\s)`, 'g'), | |
regexpUnderscored = new RegExp(`_new_${assoc}_(\\w*)`, 'g'), | |
newContents = [] | |
if (!regexpBraced.test(content)) { | |
regexpBraced = new RegExp(`\\[new_${assocs}\\](.*?\\s)`, 'g') | |
regexpUnderscored = new RegExp(`_new_${assocs}_(\\w*)`, 'g') | |
} | |
count = isNaN(count) ? 1 : Math.max(count, 1) | |
var newContent | |
while (count) { | |
newId = this.createNewId() | |
newContent = content.replace(regexpBraced, this.newcontentBraced(newId)); | |
newContent = newContent.replace(regexpUnderscored, this.newcontentUnderscored(newId)); | |
newContents.push(newContent); | |
count -= 1; | |
} | |
var insertionNodeElem = this.insertionFunc(insertionNode, insertionTraversal, $this) | |
newContents.forEach((html) => { | |
if (typeof this.beforeInsert == 'function') | |
html = this.beforeInsert(html) | |
if (!html) | |
return | |
var newNode = this.insertionFunc(this.insertionNode, html) | |
if (typeof this.afterInsert == 'function') | |
this.afterInsert(newNode) | |
}) | |
} | |
removeFields = function(e) { | |
var removeLink = e.target | |
var toRemove = removeLink.parentNode | |
var wrapperClass = removeLink.getAttribute('data-wrapper-class') || 'nested-fields' | |
while (!toRemove.classList.includes(wrapperClass)) { | |
toRemove = toRemove.parentNode | |
if (!toRemove) { | |
throw new Error('Cannot find element to remove, please check `data-wrapper-class` on `link_to_remove_association`') | |
} | |
} | |
e.preventDefault() | |
e.stopPropagation() | |
var container = toRemove.parentNode | |
if (typeof this.beforeRemove == 'function') | |
toRemove = this.beforeRemove(toRemove) | |
if (!toRemove) | |
return | |
var timeout = parseInt(container.getAttribute('data-remove-timeout')) | |
if (isNaN(timeout)) timeout = 0 | |
setTimeout(() => { | |
if (removeLink.classList.includes('dynamic')) | |
container.removeChild(toRemove) | |
else { | |
var input = toRemove.previousSibling | |
if (input.matches('input[type="hidden"]')) | |
input.value = '1' | |
toDelete.style.display = 'none' | |
} | |
if (typeof this.afterRemove == 'function') | |
this.afterRemove(toDelete) | |
}, timeout) | |
} | |
} | |
export default Cocoon |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment