Skip to content
Open
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
32 changes: 15 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,39 @@ npm install react-popover-control
```javascript
import Popover from 'react-popover-control';

actions = [
const actions = [
{
title: 'Edit',
func: () => {
...
func() {
// some action here
}
},
{
title: 'Delete',
func: () => {
...
func() {
// some action here
}
}
];

class Page extends React.Component {
render() {
return (
<Popover actions={actions}>
<a>Click Here</a>
</Popover>
<div>
Some component...

<Popover actions={actions}>
<a>Click Here</a>
</Popover>
</div>
)
}
}
};
```

## Styling

Default styles are included in `css/ReactPopoverControl.scss`.

If you are using webpack with sass-loader, you can load the style in like this:

```javascript
require('react-popover-control/css/ReactPopoverControl.scss')
```
Default styles are included as jss, but you can opt out by... (TODO)

## A good popover...

Expand All @@ -61,4 +59,4 @@ require('react-popover-control/css/ReactPopoverControl.scss')
- Arrow key control
- Scrolling when there are too many items
- Using keyboard letters as shortcuts to certain fields
- Filtering items with an input field
- Filtering items with an input field
21 changes: 0 additions & 21 deletions css/ReactPopoverControl.scss

This file was deleted.

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-popover-control",
"version": "0.1.1",
"version": "0.1.4",
"description": "A popover control for React",
"repository": {
"type": "git",
Expand All @@ -15,8 +15,15 @@
],
"main": "lib/index.js",
"dependencies": {
"classnames": "^2.1.3",
"jss": "^2.1.2",
"jss-camel-case": "^0.1.2",
"jss-nested": "^0.1.8",
"jss-px": "^0.1.3",
"jss-vendor-prefixer": "^0.2.2",
"keycode": "^2.1.0",
"lodash": "^3.8.0"
"lodash": "^3.8.0",
"react-jss": "^1.0.4"
},
"peerDependencies": {
"react": "^0.13.2",
Expand Down
6 changes: 4 additions & 2 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
"description": "A simple, idiomatic popover control for React",
"scripts": {
"start": "NODE_ENV=development webpack-dev-server --progress",
"build-dev": "NODE_ENV=development webpack --progress",
"build-prod": "NODE_ENV=production webpack -p --progress"
},
"dependencies": {
"active-event-stack": "^1.0.0",
"react": "^0.13.3",
"react-popover-control": "^0.1.0",
"react-router": "^0.13.3"
"react-popover-control": "^0.1.1",
"react-router": "^0.13.3",
"source-map-support": "^0.3.2"
},
"devDependencies": {
"autoprefixer-core": "^5.2.1",
Expand Down
4 changes: 3 additions & 1 deletion site/src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import routes from './routes';
require('../css/reset.scss');

export default function(path, props, done) {
console.log(typeof global);

Router.run(routes, path, Handler => {
done(
'<!doctype html>' +
Expand All @@ -17,4 +19,4 @@ if (typeof document != 'undefined') {
Router.run(routes, Router.HashLocation, Handler => {
React.render(<Handler/>, document);
});
}
}
6 changes: 3 additions & 3 deletions site/src/js/pages/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import marked from 'marked';
import customMarkedRenderer from '../customMarkedRenderer';

import Popover from 'react-popover-control';
require('react-popover-control/css/ReactPopoverControl.scss');

require('../../css/HomePage.scss')

const HomePageMarkdown = require('../markdown/HomePage.md');
Expand All @@ -30,7 +30,7 @@ export default class HomePage extends React.Component {
render() {
return (
<div id="home-page">
<QWHeader
<QWHeader
name="React Popover Control"
description="A simple, idiomatic popover control for React"
github="https://github.com/qimingweng/react-popover-control"/>
Expand All @@ -57,4 +57,4 @@ export default class HomePage extends React.Component {
</div>
)
}
}
}
16 changes: 13 additions & 3 deletions site/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var path = require('path');

var ExtractTextPlugin = require('extract-text-webpack-plugin');
var StaticRenderWebpackPlugin = require('static-render-webpack-plugin');
var webpack = require('webpack');
Expand Down Expand Up @@ -27,15 +29,23 @@ module.exports = {
{test: /\.md$/, loader: 'raw'}
]
},
resolveLoader: {
alias: {
babel: __dirname + '/node_modules/babel-loader'
}
},
resolve: {
alias: {
'react-popover-control$': path.join(__dirname, '../src/index.js'),
react: __dirname + '/node_modules/react'
}
},
devServer: {
port: 9000
port: 9000,
contentBase: './build'
},
devtool: IS_DEV ? 'cheap-module-source-map' : undefined,
devtool: 'sourcemap',
// devtool: IS_DEV ? 'cheap-module-source-map' : undefined,
postcss: function() {
return [autoprefixer];
},
Expand All @@ -44,4 +54,4 @@ module.exports = {
new ExtractTextPlugin('style.css'),
new StaticRenderWebpackPlugin('bundle.js', routes)
]
}
}
79 changes: 57 additions & 22 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import React, {PropTypes} from 'react';
import {debounce, map} from 'lodash';
import keycode from 'keycode';
import EventStack from 'active-event-stack';
import classNames from 'classnames';

let useSheet;
if (typeof window === 'undefined') {
useSheet = x => x => x;
} else {
useSheet = require('./useSheet');
}

const PopoverActionsType = PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string.isRequired,
Expand All @@ -11,7 +19,8 @@ const PopoverActionsType = PropTypes.arrayOf(PropTypes.shape({
export default class PopoverControl extends React.Component {
static propTypes = {
actions: PopoverActionsType.isRequired,
className: PropTypes.string
className: PropTypes.string,
sheet: PropTypes.object
}
state = {
isPopped: false
Expand Down Expand Up @@ -45,29 +54,23 @@ export default class PopoverControl extends React.Component {
}
}, 100);

document.addEventListener('scroll', this._debouncedScroll, true);
document.addEventListener('scroll', this._debouncedScroll);
window.addEventListener('resize', this._debouncedScroll);
}
componentWillUnmount = () => {
document.removeEventListener('scroll', this._debouncedScroll, true);
document.removeEventListener('scroll', this._debouncedScroll);
window.removeEventListener('resize', this._debouncedScroll);
}
render = () => {
const {top, left, width, height} = this.state;

let popoverControlStyle = {
position: 'relative'
}

return (
<div className={this.props.className}
style={popoverControlStyle}>
<div ref='self'
onClick={this.onClick}>
<div {...this.props}>
<div ref="self" onClick={this.onClick}>
{this.props.children}
</div>
{this.state.isPopped ?
<PopoverList
<PopoverList
parentFrame={{top, left, width, height}}
actions={this.props.actions}
offsetY={this.props.offsetY}
Expand All @@ -76,8 +79,31 @@ export default class PopoverControl extends React.Component {
</div>
)
}
}
};

const PopoverListStyles = {
base: {
backgroundColor: 'white',
minWidth: 150,
overflow: 'hidden',
boxShadow: '0px 1px 4px rgba(0, 0, 0, 0.25)',
borderRadius: 5
},
item: {
display: 'block',
height: 40,
lineHeight: 40,
padding: '0 10px',
color: '#1A2D36',
'&:hover': {
textDecoration: 'none',
color: '#1A2D36',
backgroundColor: '#e1f0f7',
}
}
};

@useSheet(PopoverListStyles)
class PopoverList extends React.Component {
static propTypes = {
actions: PopoverActionsType.isRequired,
Expand All @@ -86,13 +112,19 @@ class PopoverList extends React.Component {
getPopoverReferenceFrame: PropTypes.func.isRequired
}).isRequired,
windowMargin: PropTypes.number.isRequired,
launcherMargin: PropTypes.number.isRequired
launcherMargin: PropTypes.number.isRequired,
zIndex: PropTypes.number.isRequired
}
static defaultProps = {
zIndex: 10,
windowMargin: 20,
launcherMargin: 10
}
state = {
top: null,
left: null,
width: null,
height: null,
offsetX: 0,
isFlipped: false, // usually bottom facing, but if true, then the popover should appear above
}
Expand All @@ -103,8 +135,8 @@ class PopoverList extends React.Component {
]);

// Once the element is in the dom, we can measure its height
const {width, height} = React.findDOMNode(this).getBoundingClientRect();
this.setState({width, height});
const {top, left, width, height} = React.findDOMNode(this).getBoundingClientRect();
this.setState({top, left, width, height});
}
componentWillUnmount() {
EventStack.removeListenable(this.eventToken);
Expand All @@ -119,6 +151,7 @@ class PopoverList extends React.Component {
this.props.delegate.shouldHidePopover();
}
render() {
const {props: {sheet: {classes}}} = this;
const {windowMargin, launcherMargin, parentFrame, actions} = this.props;
const {width, height} = this.state;

Expand All @@ -128,7 +161,7 @@ class PopoverList extends React.Component {
};

// State
let offsetY = parentFrame.height + launcherMargin,
let offsetY = parentFrame.height + launcherMargin,
offsetX = 0;

// Flipping
Expand All @@ -144,18 +177,20 @@ class PopoverList extends React.Component {
position: 'absolute',
top: 0,
left: 0,
transform: `translate(${offsetX}px, ${offsetY}px)`
transform: `translate(${offsetX}px, ${offsetY}px)`,
zIndex: this.props.zIndex
};

return (
<div style={style} ref="self" className="ReactPopoverList">
<div style={style} ref="self" className={classes.base}>
{map(actions, (action, i) =>
<a key={i}
onClick={this.onActionClick.bind(null, action)}>
<a key={i}
onClick={this.onActionClick.bind(null, action)}
className={classes.item}>
{action.title}
</a>
)}
</div>
)
}
}
};
17 changes: 17 additions & 0 deletions src/useSheet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Jss} from 'jss';
import reactJss from 'react-jss';
import jssPx from 'jss-px';
import jssCamelCase from 'jss-camel-case';
import jssNested from 'jss-nested';

const jss = new Jss();

if (typeof window !== 'undefined') {
const vendorPrefixer = require('jss-vendor-prefixer');
jss.use(vendorPrefixer);
}
jss.use(jssPx);
jss.use(jssCamelCase);
jss.use(jssNested);

export default reactJss(jss);