- {this._getHomePageHero()}
+
{this._getHomePurpose()}
{this._getHomeFeatures()}
{this._getHomeContribute()}
@@ -32,88 +25,6 @@ const HomePage = React.createClass({
);
},
- _getHomePageHero() {
- let styles = {
- root: {
- backgroundColor: Colors.cyan500,
- overflow: 'hidden',
- },
- svgLogo: {
- marginLeft: (window.innerWidth * 0.5) - 130 + 'px',
- width: 420,
- },
- tagline: {
- margin: '16px auto 0 auto',
- textAlign: 'center',
- maxWidth: 575,
- },
- label: {
- color: DefaultRawTheme.palette.primary1Color,
- },
- githubStyle: {
- margin: '16px 32px 0px 8px',
- },
- demoStyle: {
- margin: '16px 32px 0px 32px',
- },
- h1: {
- color: Colors.darkWhite,
- fontWeight: Typography.fontWeightLight,
- },
- h2: {
- fontSize: 20,
- lineHeight: '28px',
- paddingTop: 19,
- marginBottom: 13,
- letterSpacing: 0,
- },
- nowrap: {
- whiteSpace: 'nowrap',
- },
- taglineWhenLarge: {
- marginTop: 32,
- },
- h1WhenLarge: {
- fontSize: 56,
- },
- h2WhenLarge: {
- fontSize: 24,
- lineHeight: '32px',
- paddingTop: 16,
- marginBottom: 12,
- },
- };
-
- styles.h2 = this.mergeStyles(styles.h1, styles.h2);
-
- if (this.isDeviceSize(StyleResizable.statics.Sizes.LARGE)) {
- styles.tagline = this.mergeStyles(styles.tagline, styles.taglineWhenLarge);
- styles.h1 = this.mergeStyles(styles.h1, styles.h1WhenLarge);
- styles.h2 = this.mergeStyles(styles.h2, styles.h2WhenLarge);
- }
-
- return (
-
-
-
-
material ui
-
- A Set of React Components
- that Implement
- Google's Material Design
-
-
-
-
- );
- },
-
_getHomePurpose() {
let styles = {
root: {
@@ -205,10 +116,6 @@ const HomePage = React.createClass({
);
},
-
- _onDemoClick() {
- this.history.pushState(null, '/components');
- },
});
export default HomePage;
diff --git a/docs/src/app/components/pages/page-with-nav.jsx b/docs/src/app/components/pages/page-with-nav.jsx
index 9b03c36f8e832a..ca627e35438dc0 100644
--- a/docs/src/app/components/pages/page-with-nav.jsx
+++ b/docs/src/app/components/pages/page-with-nav.jsx
@@ -39,7 +39,6 @@ let PageWithNav = React.createClass({
let subNavWidth = Spacing.desktopKeylineIncrement * 3 + 'px';
let styles = {
root: {
- paddingTop: Spacing.desktopKeylineIncrement + 'px',
},
rootWhenMedium: {
position: 'relative',
@@ -59,7 +58,7 @@ let PageWithNav = React.createClass({
secondaryNavWhenMedium: {
borderTop: 'none',
position: 'absolute',
- top: '64px',
+ top: '0px',
width: subNavWidth,
},
contentWhenMedium: {
diff --git a/src/app-bar.jsx b/src/app-bar.jsx
index 046560617b5b2c..2d1674d5cdca04 100644
--- a/src/app-bar.jsx
+++ b/src/app-bar.jsx
@@ -81,6 +81,14 @@ const AppBar = React.createClass({
*/
onTitleTouchTap: React.PropTypes.func,
+ /**
+ * Specify position and behavior. Fixed - will have a fixed position at the top of viewport.
+ * Static - will have a static position. Waterfall - will have a fixed position at the top
+ * of viewport and will decrease its height on window scroll down (see waterfall prop for
+ * additional settings).
+ */
+ position: React.PropTypes.oneOf(['fixed', 'static', 'waterfall']),
+
/**
* Determines whether or not to display the Menu icon next to the title.
* Setting this prop to false will hide the icon.
@@ -95,13 +103,36 @@ const AppBar = React.createClass({
/**
* The title to display on the app bar.
*/
- title: React.PropTypes.node,
+ title: React.PropTypes.oneOfType([
+ React.PropTypes.node,
+ React.PropTypes.func,
+ ]),
/**
* Override the inline-styles of the app bar's title element.
*/
titleStyle: React.PropTypes.object,
+ /**
+ * Settings object for position waterfall. Should at least have minHeight
+ * and maxHeight properties, both numeric. These specify min and max visual heigth
+ * of the component while window scrolling. Optional children property can be a node
+ * or a function (will receive component styles object as argument) returning a node. This node will
+ * be inserted in the slide (scrolled) element of the component. Optional onHeightChange property
+ * is a function called when visual height of the component changes on scroll. This function will
+ * receive as arguments an object with height, minHeight, maxHeight and childrenEl (DOM element of
+ * the component) properties. Using onHeightChange, animation effects can be achieved
+ * by altering style properties of specific DOM elements.
+ */
+ waterfall: React.PropTypes.shape({
+ minHeight: React.PropTypes.number,
+ maxHeight: React.PropTypes.number,
+ onHeightChange: React.PropTypes.func,
+ children: React.PropTypes.oneOfType([
+ React.PropTypes.node,
+ React.PropTypes.func,
+ ]),
+ }),
/**
* The zDepth of the app bar.
* The shadow of the app bar is also dependent on this property.
@@ -127,6 +158,7 @@ const AppBar = React.createClass({
showMenuIconButton: true,
title: '',
zDepth: 1,
+ position: 'fixed',
};
},
@@ -146,6 +178,71 @@ const AppBar = React.createClass({
);
}
}
+
+ if (this.props.waterfall && this.props.waterfall.onHeightChange) {
+ this.setupWaterfall();
+ }
+ },
+
+ componentDidUpdate: function(prevProps) {
+ if (this.props.waterfall && this.props.waterfall.onHeightChange) {
+ if (!(prevProps.waterfall && prevProps.waterfall.onHeightChange)) {
+ this.setupWaterfall();
+ }
+ } else if (prevProps.waterfall && prevProps.waterfall.onHeightChange) {
+ this.removeWaterfall();
+ }
+ },
+
+ componentWillUnmount: function() {
+ if (this.props.waterfall && this.props.waterfall.onHeightChange) {
+ this.removeWaterfall();
+ }
+ },
+
+ setupWaterfall() {
+ // in some cases scroll event is not triggered
+ // after page reloaded and kept it's scroll
+ // so we call the handler from the start
+ this.waterfallScrollHandler();
+
+ this.waterfallSlideEL.style.position = 'absolute';
+
+ window.addEventListener('scroll', this.waterfallScrollHandler);
+ },
+
+ removeWaterfall() {
+ window.removeEventListener('scroll', this.waterfallScrollHandler);
+ },
+
+ waterfallScrollHandler() {
+ if (this.waterfallRunning) { return; }
+ this.waterfallRunning = true;
+ requestAnimationFrame(() => {
+ let waterfall = this.props.waterfall;
+
+ let waterfallHeight = this.calculateWaterfallHeight();
+ if (this.waterfallHeight !== waterfallHeight) {
+ this.waterfallHeight = waterfallHeight;
+ if (waterfall.onHeightChange) {
+ waterfall.onHeightChange({
+ height: waterfallHeight,
+ maxHeight: waterfall.maxHeight,
+ minHeight: waterfall.minHeight,
+ childrenEl: this.waterfallRootEl,
+ });
+ }
+ }
+
+ this.waterfallRunning = false;
+ });
+
+ },
+
+ calculateWaterfallHeight() {
+ let waterfall = this.props.waterfall;
+ let windowScroll = window ? window.scrollY : 0;
+ return Math.max(waterfall.minHeight, waterfall.maxHeight - windowScroll);
},
getStyles() {
@@ -158,7 +255,9 @@ const AppBar = React.createClass({
let styles = {
root: {
- position: 'relative',
+ position: 'fixed',
+ top: 0,
+ left: 0,
zIndex: rawTheme.zIndex.appBar,
width: '100%',
display: 'flex',
@@ -217,6 +316,8 @@ const AppBar = React.createClass({
className,
style,
zDepth,
+ position,
+ waterfall,
children,
...other,
} = this.props;
@@ -233,15 +334,22 @@ const AppBar = React.createClass({
if (title) {
// If the title is a string, wrap in an h1 tag.
// If not, just use it as a node.
- titleElement = typeof title === 'string' || title instanceof String ?
-
{title}
-
:
-
);
+ } else {
+ let titleNode = title;
+ if (typeof title === 'function') {
+ // pass styles, otherwise inaccesible
+ titleNode = title(this.getStyles());
+ }
+ titleElement = (
- {title}
-
;
+ {titleNode}
+
);
+ }
}
if (showMenuIconButton) {
@@ -304,19 +412,89 @@ const AppBar = React.createClass({
);
}
- return (
-
{ this.waterfallRootEl = el; }}
+ style={{
+ height: waterfall.maxHeight,
+ }}>
+
+ {/* this is the visual element that will slide.
+ position will be transformed to absolute in setupWaterfall
+ */}
+ { this.waterfallSlideEL = el; }}
+ style={{
+ position: 'fixed',
+ top: 0,
+ left: 0,
+ zIndex: paperElStyle.zIndex + 1,
+ width: '100%',
+ height: waterfall.maxHeight,
+ paddingTop: waterfall.minHeight,
+ boxSizing: 'border-box',
+ backgroundColor: paperElStyle.backgroundColor,
+ }}>{waterfallChildren}
+ {/* this is the container for icons and children
+ * same styles ar for root but with no background - transparent */}
+
+ {menuElementLeft}
+ {titleElement}
+ {menuElementRight}
+ {children}
+
+
+ );
+ } else {
+ let paperEl = (
{menuElementLeft}
{titleElement}
{menuElementRight}
{children}
-
- );
+ );
+
+ if (position === 'fixed') {
+ return (
+
{paperEl}
+ );
+ } else if (position === 'static') {
+ return paperEl;
+ }
+ }
},
_onLeftIconButtonTouchTap(event) {
diff --git a/src/app-canvas.jsx b/src/app-canvas.jsx
index 370ce35142566e..270c454d342038 100644
--- a/src/app-canvas.jsx
+++ b/src/app-canvas.jsx
@@ -47,26 +47,9 @@ const AppCanvas = React.createClass({
direction: 'ltr',
};
- let newChildren = React.Children.map(this.props.children, (currentChild) => {
- if (!currentChild) { // If undefined, skip it
- return null;
- }
-
- switch (currentChild.type.displayName) {
- case 'AppBar' :
- return React.cloneElement(currentChild, {
- style: this.mergeStyles(currentChild.props.style, {
- position: 'fixed',
- }),
- });
- default:
- return currentChild;
- }
- }, this);
-
return (
- {newChildren}
+ {this.props.children}
);
},
diff --git a/test/theming-v12-spec.js b/test/theming-v12-spec.js
index 771cf5f8e04283..4f430d307e85fa 100644
--- a/test/theming-v12-spec.js
+++ b/test/theming-v12-spec.js
@@ -4,12 +4,14 @@
const AppBar = require('app-bar');
const RaisedButton = require('raised-button');
const React = require('react');
+const ReactDOM = require('react-dom');
const TestUtils = require('react-addons-test-utils');
const ThemeManager = require('styles/theme-manager');
const ThemeDecorator = require('styles/theme-decorator');
const DarkRawTheme = require('styles/raw-themes/dark-raw-theme');
const LightRawTheme = require('styles/raw-themes/light-raw-theme');
const Colors = require('styles/colors');
+const Paper = require('paper');
describe('Theming', () => {
describe('ThemeManager', () => {
@@ -38,10 +40,11 @@ describe('Theming', () => {
describe('When no theme is specified, AppBar', () => {
it('should display with default light theme', () => {
let renderedAppbar = TestUtils.renderIntoDocument(
);
- let appbarDivs = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'div');
- let firstDiv = appbarDivs[0];
+ let paperEl = ReactDOM.findDOMNode(
+ TestUtils.scryRenderedComponentsWithType(renderedAppbar, Paper)[0]
+ );
- expect(firstDiv.style.backgroundColor).to.equal('rgb(0, 188, 212)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 188, 212)');
});
});
@@ -52,22 +55,24 @@ describe('Theming', () => {
it('should display with passed down dark theme', () => {
let renderedAppbar = TestUtils.renderIntoDocument(
);
- let appbarDivs = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'div');
- let firstDiv = appbarDivs[0];
+ let paperEl = ReactDOM.findDOMNode(
+ TestUtils.scryRenderedComponentsWithType(renderedAppbar, Paper)[0]
+ );
- expect(firstDiv.style.backgroundColor).to.equal('rgb(0, 151, 167)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 151, 167)');
});
it('should display with passed down dark theme and overriden specific attribute', () => {
let renderedAppbar = TestUtils.renderIntoDocument(
);
- let appbarDivs = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'div');
- let firstDiv = appbarDivs[0];
+ let paperEl = ReactDOM.findDOMNode(
+ TestUtils.scryRenderedComponentsWithType(renderedAppbar, Paper)[0]
+ );
let appbarH1s = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'h1');
let firstH1 = appbarH1s[0];
- expect(firstDiv.style.backgroundColor).to.equal('rgb(0, 151, 167)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 151, 167)');
expect(firstH1.style.color).to.equal('rgb(98, 0, 234)');
});
@@ -77,21 +82,23 @@ describe('Theming', () => {
it('should display with passed down dark theme', () => {
let renderedAppbar = TestUtils.renderIntoDocument(
);
- let appbarDivs = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'div');
- let firstDiv = appbarDivs[0];
+ let paperEl = ReactDOM.findDOMNode(
+ TestUtils.scryRenderedComponentsWithType(renderedAppbar, Paper)[0]
+ );
- expect(firstDiv.style.backgroundColor).to.equal('rgb(0, 151, 167)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 151, 167)');
});
it('should display with passed down dark theme and overriden specific attribute', () => {
let renderedAppbar = TestUtils.renderIntoDocument(
);
- let appbarDivs = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'div');
- let firstDiv = appbarDivs[0];
+ let paperEl = ReactDOM.findDOMNode(
+ TestUtils.scryRenderedComponentsWithType(renderedAppbar, Paper)[0]
+ );
let appbarH1s = TestUtils.scryRenderedDOMComponentsWithTag(renderedAppbar, 'h1');
let firstH1 = appbarH1s[0];
- expect(firstDiv.style.backgroundColor).to.equal('rgb(0, 151, 167)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 151, 167)');
expect(firstH1.style.color).to.equal('rgb(98, 0, 234)');
});
@@ -102,21 +109,22 @@ describe('Theming', () => {
it('should display with updated theme', () => {
let renderedComponent = TestUtils.renderIntoDocument(
);
- let componentDivs = TestUtils.scryRenderedDOMComponentsWithTag(renderedComponent, 'div');
- let appbarDiv = componentDivs[1];
+ let paperEl = ReactDOM.findDOMNode(
+ TestUtils.scryRenderedComponentsWithType(renderedComponent, Paper)[0]
+ );
let buttonNode = (TestUtils.scryRenderedDOMComponentsWithTag(renderedComponent, 'button'))[1];
let appbarH1s = TestUtils.scryRenderedDOMComponentsWithTag(renderedComponent, 'h1');
let firstH1 = appbarH1s[0];
- expect(appbarDiv.style.backgroundColor).to.equal('rgb(0, 151, 167)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 151, 167)');
expect(firstH1.style.color).to.equal('rgb(48, 48, 48)');
//simulate button click
TestUtils.Simulate.click(buttonNode);
//now new theme should be applied and text color of app bar should be changed
- expect(appbarDiv.style.backgroundColor).to.equal('rgb(0, 188, 212)');
+ expect(paperEl.style.backgroundColor).to.equal('rgb(0, 188, 212)');
expect(firstH1.style.color).to.equal('rgb(98, 0, 234)');
});
});