diff --git a/README.md b/README.md index bd26a9f..0cd56d0 100644 --- a/README.md +++ b/README.md @@ -317,6 +317,14 @@ If defined, allows to specify a different animation delay for each attribute If defined, allows to specify a different animation easing for each attribute +

+ # children +

+ +- Type: Any Valid React Node + +If defined, the child node(s) will be created for each entry in dataset, allowing for complex nesting hierarchies of react-animated-dataset + ## Contributing If you make some edits and wish to test them locally you can run `yarn test`. diff --git a/build/AnimatedDataset.js b/build/AnimatedDataset.js index f0055f3..ca36501 100644 --- a/build/AnimatedDataset.js +++ b/build/AnimatedDataset.js @@ -94,7 +94,8 @@ function AnimatedDataset(_ref) { _ref$easingByAttr = _ref.easingByAttr, easingByAttr = _ref$easingByAttr === void 0 ? {} : _ref$easingByAttr, _ref$easing = _ref.easing, - easing = _ref$easing === void 0 ? easeCubic : _ref$easing; + easing = _ref$easing === void 0 ? easeCubic : _ref$easing, + children = _ref.children; var ref = /*#__PURE__*/React.createRef(); var refOldAttrs = React.useRef(); React.useLayoutEffect(function () { @@ -111,8 +112,10 @@ function AnimatedDataset(_ref) { var eventsList = Object.keys(events); var oldAttrs = refOldAttrs.current || {}; var animate = function animate() { - select(ref.current).selectAll(tag).data(dataset, keyFn).join(function (enter) { - return enter.append(tag).text(attrs.text).call(function (sel) { + select(ref.current).selectAll(tag).data(dataset, function (d) { + return d && keyFn(d) || select(this).attr("data-key"); + }).join(function (enter) { + var enterEls = enter.append(tag).call(function (sel) { attrsList.forEach(function (a) { sel.attr(a, init.hasOwnProperty(a) ? init[a] : oldAttrs.hasOwnProperty(a) ? oldAttrs[a] : attrs[a]); }); @@ -126,13 +129,17 @@ function AnimatedDataset(_ref) { tran.attr(a, attrs[a]); }); }); + if (attrs.text) enterEls.text(attrs.text); + return enterEls; }, function (update) { - return update.text(attrs.text).call(function (sel) { + var updateEls = update.call(function (sel) { attrsList.forEach(function (a) { var tran = disableAnimation ? sel : sel.transition(a).ease(easingByAttrParsed.hasOwnProperty(a) ? easingByAttrParsed[a] : easing).delay(delayByAttrParsed.hasOwnProperty(a) ? delayByAttrParsed[a] : delay).duration(durationByAttrParsed.hasOwnProperty(a) ? durationByAttrParsed[a] : duration); tran.attr(a, attrs[a]); }); }); + if (attrs.text) updateEls.text(attrs.text); + return updateEls; }, function (exit) { return exit.call(function (sel) { attrsList.forEach(function (a) { @@ -151,7 +158,19 @@ function AnimatedDataset(_ref) { }, [dataset, unparsedInit, keyFn, ref, tag, unparsedAttrs, duration, disableAnimation, unparsedEvents, delay, easing, durationByAttr, delayByAttr, easingByAttr]); return /*#__PURE__*/React.createElement('g', { ref: ref - }); + }, + // Create the structure first so that react can add the children + children && dataset.map(function (data) { + return /*#__PURE__*/React.createElement(tag, { + key: keyFn(data), + 'data-key': keyFn(data) + }, children && React.Children.toArray(children).map(function (child) { + return /*#__PURE__*/React.cloneElement(child, { + key: child.toString(), + dataset: [data] + }); + })); + })); } export { AnimatedDataset }; diff --git a/src/AnimatedDataset.js b/src/AnimatedDataset.js index d90d18c..e1830dd 100644 --- a/src/AnimatedDataset.js +++ b/src/AnimatedDataset.js @@ -19,6 +19,7 @@ export function AnimatedDataset({ delayByAttr = {}, easingByAttr = {}, easing = easeCubic, + children, }) { const ref = React.createRef() const refOldAttrs = React.useRef() @@ -40,12 +41,11 @@ export function AnimatedDataset({ const animate = () => { select(ref.current) .selectAll(tag) - .data(dataset, keyFn) + .data(dataset, function (d) { return (d && keyFn(d)) || select(this).attr("data-key"); }) .join( - enter => - enter + enter => { + const enterEls = enter .append(tag) - .text(attrs.text) .call(sel => { attrsList.forEach(a => { sel.attr( @@ -79,23 +79,32 @@ export function AnimatedDataset({ tran.attr(a, attrs[a]) }) - }), - update => - update.text(attrs.text).call(sel => { - attrsList.forEach(a => { - const tran = disableAnimation - ? sel - : sel - .transition(a) - .ease(easingByAttrParsed.hasOwnProperty(a) ? easingByAttrParsed[a] : easing) - .delay(delayByAttrParsed.hasOwnProperty(a) ? delayByAttrParsed[a] : delay) - .duration( - durationByAttrParsed.hasOwnProperty(a) ? durationByAttrParsed[a] : duration - ) + }) + if (attrs.text) enterEls.text(attrs.text) + return enterEls + }, + + update => { + const updateEls = update + .call(sel => { + attrsList.forEach(a => { + const tran = disableAnimation + ? sel + : sel + .transition(a) + .ease(easingByAttrParsed.hasOwnProperty(a) ? easingByAttrParsed[a] : easing) + .delay(delayByAttrParsed.hasOwnProperty(a) ? delayByAttrParsed[a] : delay) + .duration( + durationByAttrParsed.hasOwnProperty(a) ? durationByAttrParsed[a] : duration + ) - tran.attr(a, attrs[a]) + tran.attr(a, attrs[a]) + } + ) }) - }), + if (attrs.text) updateEls.text(attrs.text) + return updateEls; + }, exit => exit.call(sel => { @@ -139,5 +148,17 @@ export function AnimatedDataset({ easingByAttr, ]) - return React.createElement('g', { ref }) + return React.createElement( + 'g', + { ref }, + // Create the structure first so that react can add the children + children && dataset.map( + (data) => React.createElement( + tag, + { key: keyFn(data), 'data-key': keyFn(data) }, + children && React.Children.toArray(children) + .map(child => React.cloneElement(child, { key: child.toString(), dataset: [data] })) + ) + ) + ) } diff --git a/tests/AnimatedDataset.test.js b/tests/AnimatedDataset.test.js index b672996..2122a72 100644 --- a/tests/AnimatedDataset.test.js +++ b/tests/AnimatedDataset.test.js @@ -130,4 +130,29 @@ describe(AnimatedDataset, () => { expect(callArguments[0]).toEqual(dataset[index]) expect(callArguments[1]).toEqual(index) }) + + it('should allow nested nodes', () => { + const wrapper = mount( + + t} + disableAnimation + > + t }} + keyFn={t => t} + disableAnimation + /> + + + ) + + expect(wrapper.find('g').first().html()).toMatchInlineSnapshot( + `"Hello WorldGoodbye World"` + ) + }) })