Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

data-reflex-dataset and data-reflex-dataset-all #478

Merged
merged 12 commits into from
May 10, 2021
Merged
108 changes: 97 additions & 11 deletions javascript/attributes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { defaultSchema } from './schema'
import reflexes from './reflexes'
import { elementToXPath, XPathToArray } from './utils'
import Debug from './debug'
import Deprecate from './deprecate'

const multipleInstances = element => {
if (['checkbox', 'radio'].includes(element.type)) {
Expand Down Expand Up @@ -68,22 +70,106 @@ export const extractElementAttributes = element => {
return attrs
}

// Extracts the dataset of an element and combines it with the data attributes from all parents if requested.
// Returns an array of elements for the provided tokens.
// Tokens is an array of space separated string coming from the `data-reflex-dataset`
// or `data-reflex-dataset-all` attribute.
//
export const extractElementDataset = (element, datasetAttribute = null) => {
let attrs = extractDataAttributes(element) || {}
const dataset = datasetAttribute && element.attributes[datasetAttribute]
const getElementsFromTokens = (element, tokens) => {
if (!tokens || tokens.length === 0) return []

if (dataset && dataset.value === 'combined') {
let parent = element.parentElement
let elements = [element]

while (parent) {
attrs = { ...extractDataAttributes(parent), ...attrs }
parent = parent.parentElement
const xPath = elementToXPath(element)

tokens.forEach(token => {
try {
switch (token) {
case 'combined':
if (Deprecate.enabled)
console.warn(
"In the next version of StimulusReflex, the 'combined' option to data-reflex-dataset will become 'ancestors'."
)
elements = [
...elements,
...XPathToArray(`${xPath}/ancestor::*`, true)
]
break
case 'ancestors':
elements = [
...elements,
...XPathToArray(`${xPath}/ancestor::*`, true)
]
break
case 'parent':
elements = [...elements, ...XPathToArray(`${xPath}/parent::*`)]
break
case 'siblings':
elements = [
...elements,
...XPathToArray(
`${xPath}/preceding-sibling::*|${xPath}/following-sibling::*`
)
]
break
case 'children':
elements = [...elements, ...XPathToArray(`${xPath}/child::*`)]
break
case 'descendants':
elements = [...elements, ...XPathToArray(`${xPath}/descendant::*`)]
break
default:
elements = [...elements, ...document.querySelectorAll(token)]
}
} catch (error) {
if (Debug.enabled) console.error(error)
}
})

return elements
}

// Extracts the dataset of an element and combines it with the data attributes from all specified tokens
//
export const extractElementDataset = element => {
const dataset = element.attributes[reflexes.app.schema.reflexDatasetAttribute]
const allDataset =
element.attributes[reflexes.app.schema.reflexDatasetAllAttribute]

const tokens = (dataset && dataset.value.split(' ')) || []
const allTokens = (allDataset && allDataset.value.split(' ')) || []

const datasetElements = getElementsFromTokens(element, tokens)
const datasetAllElements = getElementsFromTokens(element, allTokens)

const datasetAttribtues = datasetElements.reduce((acc, ele) => {
return { ...extractDataAttributes(ele), ...acc }
}, {})

const reflexElementAttribtues = extractDataAttributes(element)

const elementDataset = {
dataset: { ...reflexElementAttribtues, ...datasetAttribtues },
datasetAll: {}
}

return attrs
datasetAllElements.forEach(element => {
const elementAttributes = extractDataAttributes(element)

Object.keys(elementAttributes).forEach(key => {
const value = elementAttributes[key]

if (
elementDataset.datasetAll[key] &&
Array.isArray(elementDataset.datasetAll[key])
) {
elementDataset.datasetAll[key].push(value)
} else {
elementDataset.datasetAll[key] = [value]
}
})
})

return elementDataset
}

// Extracts all data attributes from a DOM element.
Expand Down
1 change: 1 addition & 0 deletions javascript/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export const defaultSchema = {
reflexPermanentAttribute: 'data-reflex-permanent',
reflexRootAttribute: 'data-reflex-root',
reflexDatasetAttribute: 'data-reflex-dataset',
reflexDatasetAllAttribute: 'data-reflex-dataset-all',
reflexSerializeFormAttribute: 'data-reflex-serialize-form'
}
3 changes: 1 addition & 2 deletions javascript/stimulus_reflex.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,7 @@ const register = (controller, options = {}) => {
let selectors = options['selectors'] || getReflexRoots(reflexElement)
if (typeof selectors === 'string') selectors = [selectors]
const resolveLate = options['resolveLate'] || false
const datasetAttribute = reflexes.app.schema.reflexDatasetAttribute
const dataset = extractElementDataset(reflexElement, datasetAttribute)
const dataset = extractElementDataset(reflexElement)
const xpathController = elementToXPath(controllerElement)
const xpathElement = elementToXPath(reflexElement)
const data = {
Expand Down
Loading