Skip to content
This repository was archived by the owner on Apr 14, 2020. It is now read-only.

Commit 485d259

Browse files
committed
feat: adds reshaping container composite component
1 parent 2fddeba commit 485d259

7 files changed

Lines changed: 479 additions & 16 deletions

File tree

packages/yubaba/src/Baba/index.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,22 @@ import noop from '../lib/noop';
2121
import * as babaStore from '../lib/babaStore';
2222
import { InjectedProps, withBabaManagerContext } from '../BabaManager';
2323

24-
2524
export type AnimationFunc = () => Promise<void>;
2625

27-
2826
export interface MappedAnimation {
2927
animate: AnimationFunc;
3028
beforeAnimate: AnimationFunc;
3129
afterAnimate: AnimationFunc;
3230
cleanup: () => void;
3331
}
3432

35-
3633
export type AnimationBlock = MappedAnimation[];
3734

38-
3935
export interface ChildProps {
4036
style?: InlineStyles;
4137
className?: string;
4238
}
4339

44-
4540
export interface State {
4641
shown: boolean;
4742
childProps: ChildProps;
@@ -481,6 +476,7 @@ If it's an image, try and have the image loaded before mounting, or set a static
481476

482477
return (
483478
<Collector
479+
topMostCollector
484480
receiveData={this.setData}
485481
receiveRenderChildren={this.setReactNode}
486482
receiveRef={this.setRef}
@@ -497,5 +493,4 @@ If it's an image, try and have the image loaded before mounting, or set a static
497493
}
498494
}
499495

500-
501496
export const WrappedBaba = withBabaManagerContext(Baba);

packages/yubaba/src/Collector/index.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export interface CollectorProps extends CollectorChildrenProps {
8686
data?: CollectorData;
8787
style?: InlineStyles;
8888
className?: string;
89+
topMostCollector?: boolean;
8990
}
9091

9192
export interface Collect {
@@ -113,6 +114,7 @@ export default class Collector extends React.Component<CollectorProps> {
113114
receiveRenderChildren,
114115
receiveRef,
115116
receiveData,
117+
topMostCollector,
116118
} = this.props;
117119

118120
if (typeof children !== 'function') {
@@ -126,7 +128,7 @@ export default class Collector extends React.Component<CollectorProps> {
126128
receiveRef(ref);
127129
}
128130

129-
if (collect) {
131+
if (!topMostCollector && collect) {
130132
collect.ref(ref);
131133
}
132134
},
@@ -136,13 +138,13 @@ export default class Collector extends React.Component<CollectorProps> {
136138
receiveFocalTargetRef(ref);
137139
}
138140

139-
if (collect) {
141+
if (!topMostCollector && collect) {
140142
collect.focalTargetRef(ref);
141143
}
142144
},
143145
data: childData => {
144146
const collectedData = data ? [data].concat(childData) : childData;
145-
if (collect) {
147+
if (!topMostCollector && collect) {
146148
collect.data(collectedData);
147149
}
148150

@@ -151,7 +153,7 @@ export default class Collector extends React.Component<CollectorProps> {
151153
}
152154
},
153155
renderChildren: node => {
154-
if (collect) {
156+
if (!topMostCollector && collect) {
155157
collect.renderChildren(node);
156158
}
157159

@@ -161,9 +163,10 @@ export default class Collector extends React.Component<CollectorProps> {
161163
},
162164
style: {
163165
...style,
164-
...(collect ? collect.style : {}),
166+
...(collect && !topMostCollector ? collect.style : {}),
165167
},
166-
className: className || (collect ? collect.className : undefined),
168+
className:
169+
className || (collect && !topMostCollector ? collect.className : undefined),
167170
}}
168171
>
169172
{children}
@@ -177,7 +180,7 @@ export default class Collector extends React.Component<CollectorProps> {
177180
<CollectorContext.Consumer>
178181
{collect => {
179182
if (typeof children === 'function') {
180-
if (collect) {
183+
if (!topMostCollector && collect) {
181184
const collectedData = data ? [data] : [];
182185
collect.renderChildren(children);
183186
collect.data(collectedData);
@@ -189,17 +192,18 @@ export default class Collector extends React.Component<CollectorProps> {
189192

190193
return React.Children.only(
191194
children({
192-
className: className || (collect ? collect.className : undefined),
195+
className:
196+
className || (collect && !topMostCollector ? collect.className : undefined),
193197
ref: (ref: HTMLElement) => {
194-
if (collect) {
198+
if (!topMostCollector && collect) {
195199
collect.ref(ref);
196200
}
197201

198202
if (receiveRef) {
199203
receiveRef(ref);
200204
}
201205
},
202-
style: collect ? { ...style, ...collect.style } : style || {},
206+
style: collect && !topMostCollector ? { ...style, ...collect.style } : style || {},
203207
})
204208
);
205209
}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import * as React from 'react';
2+
import Baba from '../../Baba';
3+
import Move from '../Move';
4+
import { CollectorChildrenAsFunction } from '../../Collector';
5+
import SimpleReveal from '../SimpleReveal';
6+
import { Duration } from '../types';
7+
8+
interface ReshapingContainerProps {
9+
/**
10+
* This should be a unique identifier across your whole app.
11+
*/
12+
id: string;
13+
14+
children: CollectorChildrenAsFunction;
15+
16+
/**
17+
* Defaults to "div".
18+
* Any valid HTML tag allowed.
19+
*/
20+
as: keyof JSX.IntrinsicElements;
21+
22+
/**
23+
* Used the same as the CSS property.
24+
*/
25+
background?: string;
26+
27+
/**
28+
* Used the same as the CSS property.
29+
*/
30+
boxShadow?: string;
31+
32+
/**
33+
* Padding.
34+
* Use only px values, otherwise same as the CSS property.
35+
*/
36+
padding?: string;
37+
38+
/**
39+
* Used the same as the CSS property.
40+
*/
41+
maxWidth?: string;
42+
43+
/**
44+
* Used the same as the CSS property.
45+
*/
46+
maxHeight?: string;
47+
48+
/**
49+
* Used the same as the CSS property.
50+
*/
51+
minWidth?: string;
52+
53+
/**
54+
* Used the same as the CSS property.
55+
*/
56+
minHeight?: string;
57+
58+
/**
59+
* Used the same as the CSS property.
60+
*/
61+
margin?: string;
62+
63+
/**
64+
* Takes either "dynamic" or a number in ms.
65+
* How long the animation should take over {duration}ms.
66+
* Defaults to "dynamic".
67+
*/
68+
duration?: Duration;
69+
70+
/**
71+
* Used the same as the CSS property.
72+
*/
73+
display?: string;
74+
75+
/**
76+
* Timing function to be used in the transition.
77+
*/
78+
timingFunction?: string;
79+
}
80+
81+
interface ReshapingContainerState {
82+
renderCount: number;
83+
}
84+
85+
export default class ReshapingContainer extends React.PureComponent<
86+
ReshapingContainerProps,
87+
ReshapingContainerState
88+
> {
89+
static defaultProps = {
90+
as: 'div',
91+
};
92+
93+
state: ReshapingContainerState = {
94+
renderCount: 0,
95+
};
96+
97+
/**
98+
* Incremeent render count every time a render occurs.
99+
* We're abusing react "key" to trigger animations for now.
100+
*/
101+
static getDerivedStateFromProps(_, state) {
102+
return {
103+
renderCount: state.renderCount + 1,
104+
};
105+
}
106+
107+
componentDidMount() {
108+
if (this.props.padding.indexOf('em') >= 0 || this.props.padding.indexOf('%') >= 0) {
109+
throw new Error(`Only px values are supported for props.padding in ${this.displayName}`);
110+
}
111+
}
112+
113+
/**
114+
* We're using this to increase the clip-path box of the Reveal animation so the children contents
115+
* line up with the parent container in this component.
116+
*/
117+
getInversePaddingParts() {
118+
const parts = this.props.padding.split(' ').map(p => -Number(p.replace('px', '')));
119+
120+
switch (parts.length) {
121+
case 1:
122+
parts.push(parts[0]);
123+
parts.push(parts[0]);
124+
parts.push(parts[0]);
125+
break;
126+
127+
case 2:
128+
parts.push(parts[0]);
129+
parts.push(parts[1]);
130+
break;
131+
132+
case 3:
133+
parts.push(parts[1]);
134+
break;
135+
136+
case 4:
137+
default:
138+
break;
139+
}
140+
141+
return parts;
142+
}
143+
144+
render() {
145+
const {
146+
children,
147+
background,
148+
maxWidth,
149+
maxHeight,
150+
minWidth,
151+
minHeight,
152+
boxShadow,
153+
margin,
154+
padding,
155+
duration,
156+
id,
157+
display,
158+
timingFunction,
159+
...rest
160+
} = this.props;
161+
162+
return (
163+
<Baba name={`${id}-container`} key={this.state.renderCount}>
164+
<Move duration={duration} timingFunction={timingFunction}>
165+
{baba => (
166+
<rest.as
167+
ref={baba.ref}
168+
style={{
169+
position: 'relative',
170+
minWidth,
171+
maxWidth,
172+
minHeight,
173+
maxHeight,
174+
margin,
175+
padding,
176+
display,
177+
}}
178+
>
179+
<div
180+
aria-hidden="true"
181+
className={baba.className}
182+
style={{
183+
...baba.style,
184+
background,
185+
boxShadow,
186+
position: 'absolute',
187+
top: 0,
188+
left: 0,
189+
right: 0,
190+
bottom: 0,
191+
pointerEvents: 'none',
192+
zIndex: -1,
193+
}}
194+
/>
195+
196+
<Baba
197+
name={`${id}-children`}
198+
key={this.state.renderCount}
199+
timingFunction={timingFunction}
200+
>
201+
<SimpleReveal duration={duration} offset={this.getInversePaddingParts()}>
202+
{props => children(props)}
203+
</SimpleReveal>
204+
</Baba>
205+
</rest.as>
206+
)}
207+
</Move>
208+
</Baba>
209+
);
210+
}
211+
}

0 commit comments

Comments
 (0)