Skip to content
This repository was archived by the owner on Feb 19, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
.*/node_modules/npmconf/.*
.*/node_modules/radium/modules/.*
.*/node_modules/react-hot-loader/.*
.*/node_modules/react-motion/src/.*
.*/node_modules/rimraf/.*
.*/node_modules/watchify/.*
.*/node_modules/webpack-dev-server/.*
Expand Down
62 changes: 57 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class Rectangle extends Component {
}
}

export default Subview(Rectangle);
export default Subview()(Rectangle);
```

Here's how to use `AutoDOM` components:
Expand Down Expand Up @@ -143,6 +143,59 @@ import ConstraintLayout, { Superview, AutoDOM } from "radium-constraints";

When using `AutoSVG` components, make sure to pass "g" instead of "div" to the `<Superview>`'s `container` prop.

## Animation
You can add automatic layout animation to any `Subview` or `AutoSVG`/`AutoDOM` components! The animation system works with both `<Motion>` from `react-motion` and `<VictoryAnimation>` from `victory-core`. To create Victory-animated versions of `AutoDOM` components, for example, you'd do the following:

```es6
import { animateDOM } from "radium-constraints";
import { VictoryAnimation } from "victory-core";

const VictoryAnimationAutoDOM = animateDOM({
animatorClass: VictoryAnimation,
animatorProps: (layout) => ({
data: {
width: layout.width,
height: layout.height,
top: layout.top,
right: layout.right,
bottom: layout.bottom,
left: layout.left
}
})
});

// Later, in render()
<VictoryAnimationAutoDOM.p
name="victory-animation-note"
style={{...styles.box, fontSize: "16px", border: 0}}
intrinsicWidth={300}
intrinsicHeight={45}
constraints={this.state.dynamicConstraints}
>
This is a subview animated by VictoryAnimation!!!!!!
</VictoryAnimationAutoDOM.p>
```

When different constraints enter either the top-level or component-level `constraints` prop, the new animated component automatically tweens between the previous and newly calculated layout, diffing/removing/adding constraints behind the scenes.

If you're using the `Subview` higher-order component, you can pass an object with `animatorClass` and `animatorProps` to the first curried argument of `Subview` like so:

```es6
export default Subview({
animatorClass: VictoryAnimation,
animatorProps: (layout) => ({
data: {
width: layout.width,
height: layout.height,
top: layout.top,
right: layout.right,
bottom: layout.bottom,
left: layout.left
}
})
})(SomeCustomComponent);
```

## Demo
There are more complex examples on the demo page. Check out the code in [app.jsx](https://github.com/FormidableLabs/radium-constraints/blob/master/demo/app.jsx).

Expand All @@ -164,14 +217,13 @@ React Constraints uses an asynchronous layout engine running on a pool of WebWor
Resolving and incrementally adding/removing constraints are cheap enough to run in 60fps for most cases. However, the initial layout calculations on first load are the most expensive, and you may notice a slight delay in layout (although this does not block the main thread). We're working on a build tool that will pre-calculate initial layouts and feed them into your components to prevent this.

## Browser support
This library's browser support aligns with React's browser support minus IE 8 and 9 (neither support Web Workers.) The library requires no polyfills for its supported environments.
This library's browser support aligns with React's browser support minus IE 8 and 9 (neither support Web Workers). The library requires a Promise polyfill for non-ES6 environments.

## Roadmap <a id="roadmap"></a>
In order of priority:
- Remove dependency on autolayout.js in favor of a simple wrapper around the Kiwi constraint solver.
- Support SVG `path` elements in AutoSVG.
- Create build tool to pre-calculate initial layouts.
- Decide on an animation strategy (requires support for removing constraints).
- Support SVG `path` elements in AutoSVG.
- Remove dependency on autolayout.js in favor of a simple wrapper around the Kiwi constraint solver.
- Allow for self-referential subviews in the constraint props array without using the subview string.

## Constraint Builder API
Expand Down
130 changes: 118 additions & 12 deletions demo/app.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
// @flow
/* eslint-env browser */
/* eslint-disable new-cap,no-magic-numbers */
import type ConstraintBuilder from "../src/constraint-builder";
import React, { Component } from "react";
import ReactDOM from "react-dom";
import ConstraintLayout, { Superview, AutoDOM, constrain } from "../src/index.js";
import ConstraintLayout, {
Superview,
AutoDOM,
animateDOM,
constrain
} from "../src";

// Animators
import { Motion, spring, presets } from "react-motion";
import { VictoryAnimation } from "victory-core";

type State = {
windowWidth: ?number,
windowHeight: ?number
}
windowHeight: ?number,
dynamicConstraints: Array<ConstraintBuilder>,
activeConstraints: number
};

const MotionAutoDOM = animateDOM({
animatorClass: Motion,
animatorProps: (layout) => ({
style: {
width: spring(layout.width, presets.wobbly),
height: spring(layout.height, presets.wobbly),
top: spring(layout.top, presets.wobbly),
right: spring(layout.right, presets.wobbly),
bottom: spring(layout.bottom, presets.wobbly),
left: spring(layout.left, presets.wobbly)
}
})
});

const VictoryAnimationAutoDOM = animateDOM({
animatorClass: VictoryAnimation,
animatorProps: (layout) => ({
data: {
width: layout.width,
height: layout.height,
top: layout.top,
right: layout.right,
bottom: layout.bottom,
left: layout.left
}
})
});

const colors = {
formidared: "#FF4136",
Expand All @@ -31,14 +71,59 @@ const styles = {
}
};

const dynamicConstraintsQueue = [
[
constrain.subview("react-motion-note").centerX
.to.equal.superview.centerX.times(0.5),
constrain.subview("react-motion-note").centerY
.to.equal.superview.centerY,
constrain.subview("victory-animation-note").centerX
.to.equal.superview.centerX.times(1.5),
constrain.subview("victory-animation-note").centerY
.to.equal.superview.centerY
],
[
constrain.subview("react-motion-note").centerX
.to.equal.superview.centerX,
constrain.subview("react-motion-note").centerY
.to.equal.superview.centerY.times(0.5),
constrain.subview("victory-animation-note").centerX
.to.equal.superview.centerX,
constrain.subview("victory-animation-note").centerY
.to.equal.superview.centerY.times(1.5)
],
[
constrain.subview("react-motion-note").centerX
.to.equal.superview.centerX.times(1.5),
constrain.subview("react-motion-note").centerY
.to.equal.superview.centerY,
constrain.subview("victory-animation-note").centerX
.to.equal.superview.centerX.times(0.5),
constrain.subview("victory-animation-note").centerY
.to.equal.superview.centerY
],
[
constrain.subview("react-motion-note").centerX
.to.equal.superview.centerX,
constrain.subview("react-motion-note").centerY
.to.equal.superview.centerY.times(1.5),
constrain.subview("victory-animation-note").centerX
.to.equal.superview.centerX,
constrain.subview("victory-animation-note").centerY
.to.equal.superview.centerY.times(0.5)
]
];

class App extends Component {
state: State;

constructor(props) {
super(props);
this.state = {
windowWidth: window.innerWidth,
windowHeight: window.innerHeight
windowHeight: window.innerHeight,
activeConstraints: 0,
dynamicConstraints: dynamicConstraintsQueue[0]
};
}

Expand All @@ -57,6 +142,22 @@ class App extends Component {
});
}
);

setInterval(() => {
const nextActive =
this.state.activeConstraints !== dynamicConstraintsQueue.length - 1
? this.state.activeConstraints + 1 : 0;

// console.log(nextActive);

const nextConstraints = dynamicConstraintsQueue[nextActive];

// console.log(nextConstraints);
this.setState({ // eslint-disable-line react/no-did-mount-set-state
activeConstraints: nextActive,
dynamicConstraints: nextConstraints
});
}, 2000);
}

render() {
Expand All @@ -70,19 +171,24 @@ class App extends Component {
style={{
background: colors.shade1
}}
constraints={[
constrain.subview("note").centerX.to.equal.superview.centerX,
constrain.subview("note").centerY.to.equal.superview.centerY
]}
constraints={this.state.dynamicConstraints}
>
<AutoDOM.p
name="note"
<MotionAutoDOM.p
name="react-motion-note"
style={{...styles.box, fontSize: "16px", border: 0}}
intrinsicWidth={300}
intrinsicHeight={45}
>
Worst clock ever! Resize the window for full effect.
</AutoDOM.p>
This is a subview animated by React Motion!!!!!!!!!!
</MotionAutoDOM.p>
<VictoryAnimationAutoDOM.p
name="victory-animation-note"
style={{...styles.box, fontSize: "16px", border: 0}}
intrinsicWidth={300}
intrinsicHeight={45}
>
This is a subview animated by VictoryAnimation!!!!!!
</VictoryAnimationAutoDOM.p>
<AutoDOM.p
name="12"
style={styles.box}
Expand Down
8 changes: 0 additions & 8 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,5 @@
<![endif]-->
<script src="http://localhost:3000/webpack-dev-server.js"></script>
<script src="assets/main.js"></script>
<script>
// Sanity-check the component loaded...
setTimeout(function () {
var content = document.querySelector("#content");
content.innerHTML = content.innerHTML ||
"If you can see this, something is broken (or JS is not enabled)!";
}, 500);
</script>
</body>
</html>
10 changes: 5 additions & 5 deletions interfaces/autolayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ declare module "autolayout" {
declare type Relation =
| "equ"
| "leq"
| "geq"
| "geq";

declare type Priority =
| 1000
| 750
| 250
| 250;

declare type Constraint = {
view1?: ?string,
Expand Down Expand Up @@ -63,12 +63,12 @@ declare module "autolayout/lib/kiwi/View" {
declare type Relation =
| "equ"
| "leq"
| "geq"
| "geq";

declare type Priority =
| 1000
| 750
| 250
| 250;

declare type Constraint = {
view1?: ?string,
Expand Down Expand Up @@ -119,5 +119,5 @@ declare module "autolayout/lib/kiwi/View" {
addConstraints(constraints: Array<Constraint>): View;
}

declare function exports(): View
declare function exports(): View;
}
5 changes: 5 additions & 0 deletions interfaces/react-motion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module "react-motion" {
declare var Motion: ReactClass;
declare var spring: any;
declare var presets: any;
}
3 changes: 3 additions & 0 deletions interfaces/victory-core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module "victory-core" {
declare var VictoryAnimation: ReactClass;
}
1 change: 1 addition & 0 deletions interfaces/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type WorkerEventHandler = (event: WorkerEvent) => mixed;
declare class Worker {
constructor(URL: ?string): void;
onmessage: WorkerEventHandler;
onerror: WorkerEventHandler;
postMessage(messsage: Cloneable): void;
terminate(): void;
}
Expand Down
45 changes: 25 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,38 @@
"build-lib": "builder run clean-lib && babel plugins/src -d plugins/lib --copy-files && babel src -d lib --copy-files"
},
"dependencies": {
"autolayout": "FormidableLabs/autolayout.js#7a28278",
"babel-plugin-webpack-loaders": "^0.4.0",
"builder": "^2.8.0",
"builder-radium-component": "^2.0.0",
"autolayout": "FormidableLabs/autolayout.js#4f3bd46",
"babel-plugin-webpack-loaders": "^0.5.0",
"builder": "^2.10.1",
"builder-radium-component": "^2.1.2",
"coveralls": "^2.11.8",
"react": "^0.14.7",
"react-dom": "^0.14.7"
"lodash.isequal": "^4.2.0"
},
"devDependencies": {
"babel-eslint": "^6.0.0",
"babel-polyfill": "^6.6.1",
"babel-preset-es2015": "^6.6.0",
"babel-register": "^6.6.0",
"builder": "^2.8.0",
"builder-radium-component-dev": "^2.0.0",
"babel-eslint": "^6.0.4",
"babel-polyfill": "^6.9.1",
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.9.0",
"builder": "^2.10.1",
"builder-radium-component-dev": "^2.1.2",
"chai": "^3.2.0",
"enzyme": "^2.1.0",
"eslint-plugin-flow-vars": "^0.2.1",
"enzyme": "^2.3.0",
"eslint-plugin-flow-vars": "^0.4.0",
"exports-loader": "^0.6.3",
"flow-bin": "^0.22.1",
"mocha": "^2.3.3",
"react": "^0.14.6",
"react-addons-test-utils": "^0.14.7",
"react-dom": "^0.14.6",
"sinon": "^1.17.2",
"flow-bin": "^0.26.0",
"mocha": "^2.5.3",
"react": "^15.1.0",
"react-addons-test-utils": "^15.1.0",
"react-dom": "^15.1.0",
"react-motion": "^0.4.4",
"sinon": "^1.17.4",
"sinon-chai": "^2.8.0",
"victory-core": "^3.0.0",
"webpack-dev-server": "^1.14.1"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0-0",
"react-dom": "^0.14.0 || ^15.0.0-0"
},
"author": "Tyler Thompson"
}
Loading