├── .npmignore
├── example
├── app
│ ├── assets
│ │ ├── styles
│ │ │ ├── _page.scss
│ │ │ ├── _variable.scss
│ │ │ ├── _mixin.scss
│ │ │ ├── app.scss
│ │ │ ├── _common.scss
│ │ │ └── _region.scss
│ │ └── images
│ │ │ └── logo.svg
│ ├── routes
│ │ ├── Example1
│ │ │ └── index.js
│ │ └── Example2
│ │ │ └── index.js
│ ├── components
│ │ ├── Footer.js
│ │ ├── pages
│ │ │ ├── Example1
│ │ │ │ └── index.js
│ │ │ ├── Example2
│ │ │ │ └── index.js
│ │ │ └── Home
│ │ │ │ └── index.js
│ │ ├── App.js
│ │ ├── Header.js
│ │ └── common
│ │ │ └── Document.js
│ ├── index.html
│ └── app.js
├── webpack.server.js
└── webpack.config.js
├── .bowerrc
├── .gitignore
├── src
├── index.js
├── tooltip.scss
└── Tooltip.js
├── .eslintignore
├── .stylelintignore
├── .stylelintrc
├── .editorconfig
├── lib
├── index.js
├── tooltip.css
└── Tooltip.js
├── .babelrc
├── bower.json
├── webpack.config.js
├── README.md
├── dist
├── react-tooltip-component.css
└── react-tooltip-component.js
├── .eslintrc
├── package.json
└── gulpfile.babel.js
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea
2 | example
--------------------------------------------------------------------------------
/example/app/assets/styles/_page.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/*
2 | node_modules/*
3 | bower_components/*
4 | example/dist/*
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Tooltip from './Tooltip';
2 |
3 | export default Tooltip;
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | bower_components/*
3 | dist/*
4 | example/dist/*
5 | lib/*
6 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | bower_components/*
3 | dist/*
4 | example/dist/*
5 | lib/*
6 |
--------------------------------------------------------------------------------
/example/app/assets/styles/_variable.scss:
--------------------------------------------------------------------------------
1 | $black: #000;
2 | $white: #fff;
3 | $primary: $black;
4 |
--------------------------------------------------------------------------------
/example/app/assets/styles/_mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix() {
2 | &::before,
3 | &::after {
4 | content: " ";
5 | display: table;
6 | }
7 | &::after {
8 | clear: both;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example/app/routes/Example1/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | path: 'ex-1',
3 | getComponent(location, callback) {
4 | require.ensure([], require => {
5 | callback(null, require('components/pages/Example1'));
6 | }, 'page-ex-1');
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/example/app/routes/Example2/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | path: 'ex-2',
3 | getComponent(location, callback) {
4 | require.ensure([], require => {
5 | callback(null, require('components/pages/Example2'));
6 | }, 'page-ex-2');
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard",
3 | "rules": {
4 | "at-rule-empty-line-before": [
5 | "always", {
6 | "except": ["blockless-group", "all-nested"],
7 | "ignore": ["after-comment"]
8 | }
9 | ]
10 | }
11 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/example/app/assets/styles/app.scss:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | React component starter
3 | ========================================================================== */
4 | @import "variable";
5 | @import "mixin";
6 | @import "common";
7 | @import "region";
8 | @import "page";
9 |
--------------------------------------------------------------------------------
/example/app/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class Footer extends React.Component {
4 | render() {
5 | return (
6 |
11 | );
12 | }
13 | }
14 |
15 | export default Footer;
16 |
17 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
8 |
9 | var _Tooltip = require('./Tooltip');
10 |
11 | var _Tooltip2 = _interopRequireDefault(_Tooltip);
12 |
13 | exports['default'] = _Tooltip2['default'];
14 | module.exports = exports['default'];
--------------------------------------------------------------------------------
/example/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React component example
6 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example/app/components/pages/Example1/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Document from 'components/common/Document';
3 |
4 | class Example1Page extends React.Component {
5 | render() {
6 | return (
7 |
9 | Example 1
10 |
11 | );
12 | }
13 | }
14 |
15 | export default Example1Page;
16 |
17 |
--------------------------------------------------------------------------------
/example/app/components/pages/Example2/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Document from 'components/common/Document';
3 |
4 | class Example2Page extends React.Component {
5 | render() {
6 | return (
7 |
9 | Example 2
10 |
11 | );
12 | }
13 | }
14 |
15 | export default Example2Page;
16 |
17 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0,
3 | "optional": [],
4 | "env": {
5 | "development": {
6 | "plugins": ["react-transform"],
7 | "extra": {
8 | "react-transform": {
9 | "transforms": [
10 | {
11 | "transform": "react-transform-hmr",
12 | "imports": ["react"],
13 | "locals": ["module"]
14 | }
15 | ]
16 | }
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-tooltip-component",
3 | "version": "0.3.0",
4 | "description": "React component.",
5 | "main": ["dist/react-tooltip-component.js", "dist/react-tooltip-component.css"],
6 | "keywords": [
7 | "react-component",
8 | "react",
9 | "component",
10 | "tooltip"
11 | ],
12 | "license": "MIT",
13 | "ignore": [
14 | "**/.*",
15 | "node_modules",
16 | "bower_components",
17 | "test",
18 | "tests"
19 | ],
20 | "dependencies": {},
21 | "devDependencies": {}
22 | }
23 |
--------------------------------------------------------------------------------
/example/app/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Header from './Header';
3 | import Footer from './Footer';
4 |
5 | class App extends React.Component {
6 | static propTypes = {
7 | children: React.PropTypes.node
8 | };
9 |
10 | render() {
11 | return (
12 |
13 |
14 |
15 |
16 | {this.props.children}
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
25 | export default App;
26 |
--------------------------------------------------------------------------------
/example/app/assets/styles/_common.scss:
--------------------------------------------------------------------------------
1 | /* Pre Render
2 | ========================================================================== */
3 | @keyframes spinner {
4 | 0% {
5 | transform: rotate(0deg);
6 | }
7 | 100% {
8 | transform: rotate(360deg);
9 | }
10 | }
11 |
12 | .pre-render {
13 | background: rgba(255, 255, 255, 0.7);
14 | position: fixed;
15 | top: 0;
16 | left: 0;
17 | width: 100%;
18 | height: 100%;
19 | z-index: 99999;
20 | .spinner {
21 | width: 48px;
22 | height: 48px;
23 | border: 1px solid lighten($primary, 40%);
24 | border-left-color: darken($primary, 10%);
25 | border-radius: 50%;
26 | animation: spinner 700ms infinite linear;
27 | position: absolute;
28 | top: 50%;
29 | left: 50%;
30 | margin-left: -24px;
31 | margin-top: -24px;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/app/assets/styles/_region.scss:
--------------------------------------------------------------------------------
1 | /* Header
2 | ========================================================================== */
3 | .navbar {
4 | border-radius: 0;
5 | }
6 |
7 | /* Footer
8 | ========================================================================== */
9 | html,
10 | body,
11 | #app {
12 | height: 100%;
13 | }
14 |
15 | .layout-page {
16 | position: relative;
17 | min-height: 100%;
18 | padding-bottom: 60px;
19 | }
20 |
21 | .layout-main {
22 | margin-bottom: 30px;
23 | }
24 |
25 | .layout-footer {
26 | position: absolute;
27 | bottom: 0;
28 | width: 100%;
29 | height: 60px;
30 | background-color: #f8f8f8;
31 | padding: 20px 0;
32 | }
33 |
34 | /* Tooltip example
35 | ========================================================================== */
36 | .tooltips-example {
37 | text-align: center;
38 | .btn {
39 | margin: 15px;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/example/app/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {Link} from 'react-router';
3 |
4 | import logo from 'assets/images/logo.svg';
5 |
6 | class Header extends React.Component {
7 | render() {
8 | return (
9 |
10 |
25 |
26 | );
27 | }
28 | }
29 |
30 | export default Header;
31 |
32 |
--------------------------------------------------------------------------------
/example/app/components/common/Document.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class Document extends React.Component {
4 | static propTypes = {
5 | title: React.PropTypes.string,
6 | className: React.PropTypes.string,
7 | children: React.PropTypes.any.isRequired
8 | };
9 |
10 | state = {
11 | oldTitle: document.title,
12 | oldClassName: document.body.className
13 | };
14 |
15 | componentWillMount = () => {
16 | if (this.props.title) {
17 | document.title = this.props.title;
18 | }
19 | if (this.props.className) {
20 | let className = this.state.oldClassName + ' ' + this.props.className;
21 | document.body.className = className.trim().replace(' ', ' ');
22 | }
23 | };
24 |
25 | componentWillUnmount = () => {
26 | document.title = this.state.oldTitle;
27 | document.body.className = this.state.oldClassName;
28 | };
29 |
30 | render() {
31 | if (this.props.children) {
32 | return React.Children.only(this.props.children);
33 | }
34 | return null;
35 | }
36 | }
37 |
38 | export default Document;
39 |
--------------------------------------------------------------------------------
/example/app/app.js:
--------------------------------------------------------------------------------
1 | import 'babel-core/polyfill';
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 | import {createHistory} from 'history';
6 | import {Router, useRouterHistory} from 'react-router';
7 | import App from 'components/App.js';
8 | import {name} from '../../package.json';
9 |
10 | import 'bootstrap/dist/css/bootstrap.css';
11 | import 'assets/styles/app.scss';
12 |
13 | const routes = {
14 | path: '/',
15 | component: App,
16 | indexRoute: {
17 | component: require('components/pages/Home')
18 | },
19 | childRoutes: [
20 | require('routes/Example1'),
21 | require('routes/Example2')
22 | ]
23 | };
24 |
25 | const DEV = process && process.env && process.env.NODE_ENV === 'development';
26 | const history = useRouterHistory(createHistory)({
27 | basename: '/' + (DEV ? '' : name)
28 | });
29 |
30 | const run = () => {
31 | ReactDOM.render(
32 | ,
33 | document.getElementById('app')
34 | );
35 | };
36 |
37 | if (window.addEventListener) {
38 | window.addEventListener('DOMContentLoaded', run);
39 | } else {
40 | window.attachEvent('onload', run);
41 | }
42 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 | import pkg from './package.json';
3 | import camelCase from 'camelcase';
4 |
5 | const capitalizeFirstLetter = (string) => {
6 | return string.charAt(0).toUpperCase() + string.slice(1);
7 | };
8 |
9 | const webpackConfig = {
10 | output: {
11 | filename: pkg.name + '.js',
12 | library: capitalizeFirstLetter(camelCase(pkg.name)),
13 | libraryTarget: 'umd'
14 | },
15 | externals: {
16 | react: {
17 | root: 'React',
18 | commonjs: 'react',
19 | commonjs2: 'react',
20 | amd: 'react'
21 | },
22 | 'react-dom': {
23 | root: 'ReactDOM',
24 | commonjs: 'react-dom',
25 | commonjs2: 'react-dom',
26 | amd: 'react-dom'
27 | }
28 | },
29 | module: {
30 | loaders: [
31 | {
32 | test: /\.(js|jsx)$/,
33 | exclude: /(node_modules)/,
34 | loader: 'babel-loader'
35 | }
36 | ]
37 | },
38 | resolve: {
39 | modulesDirectories: ['node_modules', 'bower_components'],
40 | extensions: ['', '.jsx', '.js']
41 | },
42 | plugins: [
43 | new webpack.DefinePlugin({
44 | 'process.env': {
45 | NODE_ENV: JSON.stringify(process.env.NODE_ENV)
46 | }
47 | }),
48 | new webpack.optimize.UglifyJsPlugin({
49 | sourceMap: false,
50 | compress: {
51 | warnings: false
52 | },
53 | output: {
54 | comments: false
55 | }
56 | }),
57 | new webpack.optimize.DedupePlugin()
58 | ]
59 | };
60 |
61 | export default webpackConfig;
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Tooltip
2 |
3 | A simple tooltip component for ReactJS.
4 |
5 | ## Installation
6 |
7 | ```bash
8 | npm install --save-dev react-tooltip-component
9 | ```
10 |
11 | ## Usage
12 |
13 | ### Style
14 |
15 | #### Webpack
16 |
17 | ```js
18 | import 'react-tooltip-component/lib/tooltip.css';
19 | //require('react-tooltip-component/lib/tooltip.css');
20 | ```
21 |
22 | ### Other
23 |
24 | ```html
25 |
26 | ```
27 |
28 | ### JS
29 |
30 | ```js
31 | import Tooltip from 'react-tooltip-component';
32 |
33 |
34 |
35 |
36 | ```
37 |
38 | ### UMD
39 |
40 | ```html
41 |
42 |
43 | ```
44 |
45 | ```js
46 | const Tooltip = window.ReactTooltipComponent;
47 | ```
48 |
49 | ## Props
50 |
51 | | Name | Type | Required | Default | Description |
52 | |------|------|----------|---------|-------------|
53 | | title | string | true | | |
54 | | position | string | false | `top` | ['left', 'top', 'right', 'bottom'] |
55 | | fixed | bool | false | true | fixed or not |
56 | | container | element | false | document.body | |
57 | | children | node | true | |
58 |
59 | ## Example
60 |
61 | View [demo](http://minhtranite.github.io/react-tooltip-component) or example folder.
62 |
--------------------------------------------------------------------------------
/dist/react-tooltip-component.css:
--------------------------------------------------------------------------------
1 | .tooltip{position:absolute;z-index:1070;display:block;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px}.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{left:5px}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}
--------------------------------------------------------------------------------
/example/app/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
--------------------------------------------------------------------------------
/example/webpack.server.js:
--------------------------------------------------------------------------------
1 | import url from 'url';
2 | import express from 'express';
3 | import webpack from 'webpack';
4 | import webpackConfig from './webpack.config';
5 | import webpackDevMiddleware from 'webpack-dev-middleware';
6 | import webpackHotMiddleware from 'webpack-hot-middleware';
7 | import fs from 'fs';
8 | import path from 'path';
9 | import http from 'http';
10 | import https from 'https';
11 | import opn from 'opn';
12 | import httpProxy from 'http-proxy';
13 |
14 | const devURL = 'http://localhost:3000';
15 | const urlParts = url.parse(devURL);
16 | const proxyOptions = [];
17 |
18 | const proxy = httpProxy.createProxyServer({
19 | changeOrigin: true,
20 | ws: true
21 | });
22 |
23 | const compiler = webpack(webpackConfig);
24 |
25 | const app = express();
26 |
27 | app.use(webpackDevMiddleware(compiler, {
28 | noInfo: true,
29 | publicPath: webpackConfig.output.publicPath,
30 | stats: {
31 | colors: true,
32 | hash: false,
33 | timings: false,
34 | chunks: false,
35 | chunkModules: false,
36 | modules: false,
37 | children: false,
38 | version: false,
39 | cached: false,
40 | cachedAssets: false,
41 | reasons: false,
42 | source: false,
43 | errorDetails: false
44 | }
45 | }));
46 |
47 | app.use(webpackHotMiddleware(compiler));
48 |
49 | app.use('/assets', express.static(path.join(__dirname, 'app/assets')));
50 |
51 | proxyOptions.forEach(option => {
52 | app.all(option.path, (req, res) => {
53 | proxy.web(req, res, option, err => {
54 | console.log(err.message);
55 | res.statusCode = 502;
56 | res.end();
57 | });
58 | });
59 | });
60 |
61 | app.get('*', (req, res, next) => {
62 | let filename = path.join(compiler.outputPath, 'index.html');
63 | compiler.outputFileSystem.readFile(filename, (error, result) => {
64 | if (error) {
65 | return next(error);
66 | }
67 | res.set('content-type', 'text/html');
68 | res.send(result);
69 | res.end();
70 | });
71 | });
72 |
73 | let server = http.createServer(app);
74 | if (urlParts.protocol === 'https:') {
75 | server = https.createServer({
76 | key: fs.readFileSync(path.join(__dirname, 'key.pem')),
77 | cert: fs.readFileSync(path.join(__dirname, 'cert.pem'))
78 | }, app);
79 | }
80 |
81 | server.listen(urlParts.port, () => {
82 | console.log('Listening at ' + devURL);
83 | opn(devURL);
84 | });
85 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "node": true
6 | },
7 | "ecmaFeatures": {
8 | "arrowFunctions": true,
9 | "blockBindings": true,
10 | "classes": true,
11 | "defaultParams": true,
12 | "destructuring": true,
13 | "forOf": true,
14 | "modules": true,
15 | "objectLiteralComputedProperties": true,
16 | "objectLiteralShorthandMethods": true,
17 | "objectLiteralShorthandProperties": true,
18 | "spread": true,
19 | "superInFunctions": true,
20 | "templateStrings": true,
21 | "unicodeCodePointEscapes": true,
22 | "jsx": true
23 | },
24 | "rules": {
25 | "strict": 0,
26 | "curly": 0,
27 | "quotes": [2, "single", "avoid-escape"],
28 | "semi": 2,
29 | "no-underscore-dangle": 0,
30 | "no-unused-vars": 2,
31 | "camelcase": [2, {"properties": "never"}],
32 | "new-cap": 0,
33 | "accessor-pairs": 0,
34 | "brace-style": [2, "1tbs"],
35 | "consistent-return": 2,
36 | "dot-location": [2, "property"],
37 | "dot-notation": 2,
38 | "eol-last": 2,
39 | "indent": [2, 2, {"SwitchCase": 1}],
40 | "no-bitwise": 0,
41 | "no-multi-spaces": 2,
42 | "no-shadow": 2,
43 | "no-unused-expressions": 2,
44 | "space-after-keywords": 2,
45 | "space-before-blocks": 2,
46 | "jsx-quotes": [1, "prefer-double"],
47 | "react/display-name": 0,
48 | "react/jsx-boolean-value": [2, "always"],
49 | "react/jsx-no-undef": 2,
50 | "react/jsx-sort-props": 0,
51 | "react/jsx-sort-prop-types": 0,
52 | "react/jsx-uses-react": 2,
53 | "react/jsx-uses-vars": 2,
54 | "react/no-did-mount-set-state": 2,
55 | "react/no-did-update-set-state": 2,
56 | "react/no-multi-comp": [2, {"ignoreStateless": true}],
57 | "react/no-unknown-property": 2,
58 | "react/prop-types": 1,
59 | "react/react-in-jsx-scope": 2,
60 | "react/self-closing-comp": 2,
61 | "react/sort-comp": 0,
62 | "react/wrap-multilines": [2, {"declaration": false, "assignment": false}]
63 | },
64 | "globals": {
65 | "inject": false,
66 | "module": false,
67 | "describe": false,
68 | "it": false,
69 | "before": false,
70 | "beforeEach": false,
71 | "after": false,
72 | "afterEach": false,
73 | "expect": false,
74 | "window": false,
75 | "document": false
76 | },
77 | "plugins": [
78 | "react"
79 | ]
80 | }
81 |
--------------------------------------------------------------------------------
/lib/tooltip.css:
--------------------------------------------------------------------------------
1 | .tooltip {
2 | position: absolute;
3 | z-index: 1070;
4 | display: block;
5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6 | font-style: normal;
7 | font-weight: normal;
8 | letter-spacing: normal;
9 | line-break: auto;
10 | line-height: 1.42857143;
11 | text-align: left;
12 | text-decoration: none;
13 | text-shadow: none;
14 | text-transform: none;
15 | white-space: normal;
16 | word-break: normal;
17 | word-spacing: normal;
18 | word-wrap: normal;
19 | font-size: 12px;
20 | }
21 |
22 | .tooltip.top {
23 | margin-top: -3px;
24 | padding: 5px 0;
25 | }
26 |
27 | .tooltip.right {
28 | margin-left: 3px;
29 | padding: 0 5px;
30 | }
31 |
32 | .tooltip.bottom {
33 | margin-top: 3px;
34 | padding: 5px 0;
35 | }
36 |
37 | .tooltip.left {
38 | margin-left: -3px;
39 | padding: 0 5px;
40 | }
41 |
42 | .tooltip-inner {
43 | max-width: 200px;
44 | padding: 3px 8px;
45 | color: #fff;
46 | text-align: center;
47 | background-color: #000;
48 | border-radius: 4px;
49 | }
50 |
51 | .tooltip-arrow {
52 | position: absolute;
53 | width: 0;
54 | height: 0;
55 | border-color: transparent;
56 | border-style: solid;
57 | }
58 |
59 | .tooltip.top .tooltip-arrow {
60 | bottom: 0;
61 | left: 50%;
62 | margin-left: -5px;
63 | border-width: 5px 5px 0;
64 | border-top-color: #000;
65 | }
66 |
67 | .tooltip.top-left .tooltip-arrow {
68 | bottom: 0;
69 | right: 5px;
70 | margin-bottom: -5px;
71 | border-width: 5px 5px 0;
72 | border-top-color: #000;
73 | }
74 |
75 | .tooltip.top-right .tooltip-arrow {
76 | bottom: 0;
77 | left: 5px;
78 | margin-bottom: -5px;
79 | border-width: 5px 5px 0;
80 | border-top-color: #000;
81 | }
82 |
83 | .tooltip.right .tooltip-arrow {
84 | top: 50%;
85 | left: 0;
86 | margin-top: -5px;
87 | border-width: 5px 5px 5px 0;
88 | border-right-color: #000;
89 | }
90 |
91 | .tooltip.left .tooltip-arrow {
92 | top: 50%;
93 | right: 0;
94 | margin-top: -5px;
95 | border-width: 5px 0 5px 5px;
96 | border-left-color: #000;
97 | }
98 |
99 | .tooltip.bottom .tooltip-arrow {
100 | top: 0;
101 | left: 50%;
102 | margin-left: -5px;
103 | border-width: 0 5px 5px;
104 | border-bottom-color: #000;
105 | }
106 |
107 | .tooltip.bottom-left .tooltip-arrow {
108 | top: 0;
109 | right: 5px;
110 | margin-top: -5px;
111 | border-width: 0 5px 5px;
112 | border-bottom-color: #000;
113 | }
114 |
115 | .tooltip.bottom-right .tooltip-arrow {
116 | top: 0;
117 | left: 5px;
118 | margin-top: -5px;
119 | border-width: 0 5px 5px;
120 | border-bottom-color: #000;
121 | }
122 |
--------------------------------------------------------------------------------
/src/tooltip.scss:
--------------------------------------------------------------------------------
1 | .tooltip {
2 | position: absolute;
3 | z-index: 1070;
4 | display: block;
5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6 | font-style: normal;
7 | font-weight: normal;
8 | letter-spacing: normal;
9 | line-break: auto;
10 | line-height: 1.42857143;
11 | text-align: left;
12 | text-decoration: none;
13 | text-shadow: none;
14 | text-transform: none;
15 | white-space: normal;
16 | word-break: normal;
17 | word-spacing: normal;
18 | word-wrap: normal;
19 | font-size: 12px;
20 | }
21 |
22 | .tooltip.top {
23 | margin-top: -3px;
24 | padding: 5px 0;
25 | }
26 |
27 | .tooltip.right {
28 | margin-left: 3px;
29 | padding: 0 5px;
30 | }
31 |
32 | .tooltip.bottom {
33 | margin-top: 3px;
34 | padding: 5px 0;
35 | }
36 |
37 | .tooltip.left {
38 | margin-left: -3px;
39 | padding: 0 5px;
40 | }
41 |
42 | .tooltip-inner {
43 | max-width: 200px;
44 | padding: 3px 8px;
45 | color: #fff;
46 | text-align: center;
47 | background-color: #000;
48 | border-radius: 4px;
49 | }
50 |
51 | .tooltip-arrow {
52 | position: absolute;
53 | width: 0;
54 | height: 0;
55 | border-color: transparent;
56 | border-style: solid;
57 | }
58 |
59 | .tooltip.top .tooltip-arrow {
60 | bottom: 0;
61 | left: 50%;
62 | margin-left: -5px;
63 | border-width: 5px 5px 0;
64 | border-top-color: #000;
65 | }
66 |
67 | .tooltip.top-left .tooltip-arrow {
68 | bottom: 0;
69 | right: 5px;
70 | margin-bottom: -5px;
71 | border-width: 5px 5px 0;
72 | border-top-color: #000;
73 | }
74 |
75 | .tooltip.top-right .tooltip-arrow {
76 | bottom: 0;
77 | left: 5px;
78 | margin-bottom: -5px;
79 | border-width: 5px 5px 0;
80 | border-top-color: #000;
81 | }
82 |
83 | .tooltip.right .tooltip-arrow {
84 | top: 50%;
85 | left: 0;
86 | margin-top: -5px;
87 | border-width: 5px 5px 5px 0;
88 | border-right-color: #000;
89 | }
90 |
91 | .tooltip.left .tooltip-arrow {
92 | top: 50%;
93 | right: 0;
94 | margin-top: -5px;
95 | border-width: 5px 0 5px 5px;
96 | border-left-color: #000;
97 | }
98 |
99 | .tooltip.bottom .tooltip-arrow {
100 | top: 0;
101 | left: 50%;
102 | margin-left: -5px;
103 | border-width: 0 5px 5px;
104 | border-bottom-color: #000;
105 | }
106 |
107 | .tooltip.bottom-left .tooltip-arrow {
108 | top: 0;
109 | right: 5px;
110 | margin-top: -5px;
111 | border-width: 0 5px 5px;
112 | border-bottom-color: #000;
113 | }
114 |
115 | .tooltip.bottom-right .tooltip-arrow {
116 | top: 0;
117 | left: 5px;
118 | margin-top: -5px;
119 | border-width: 0 5px 5px;
120 | border-bottom-color: #000;
121 | }
122 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-tooltip-component",
3 | "version": "0.3.0",
4 | "description": "React tooltip component.",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "start": "NODE_ENV=development gulp start",
8 | "eslint": "NODE_ENV=development eslint .",
9 | "stylelint": "NODE_ENV=development stylelint '**/*.?(s)@(a|c)ss'",
10 | "lint": "npm run eslint && npm run stylelint",
11 | "build": "NODE_ENV=production gulp build"
12 | },
13 | "keywords": [
14 | "react-component",
15 | "react",
16 | "component",
17 | "tooltip"
18 | ],
19 | "peerDependencies": {
20 | "react": "^0.14 || ^15.0.0",
21 | "react-dom": "^0.14 || ^15.0.0"
22 | },
23 | "dependencies": {},
24 | "devDependencies": {
25 | "autoprefixer": "^6.3.1",
26 | "babel": "^5.8.34",
27 | "babel-core": "^5.8.29",
28 | "babel-eslint": "^4.1.3",
29 | "babel-loader": "^5.3.2",
30 | "babel-plugin-react-transform": "^1.1.1",
31 | "bootstrap": "^3.3.6",
32 | "camelcase": "^2.0.1",
33 | "css-loader": "^0.23.0",
34 | "cssnano": "^3.3.2",
35 | "del": "^2.1.0",
36 | "eslint": "^1.10.1",
37 | "eslint-loader": "^1.2.0",
38 | "eslint-plugin-react": "^3.15.0",
39 | "express": "^4.13.3",
40 | "extract-text-webpack-plugin": "^1.0.1",
41 | "file-loader": "^0.8.5",
42 | "gulp": "^3.9.0",
43 | "gulp-babel": "^5.2.1",
44 | "gulp-concat": "^2.6.0",
45 | "gulp-eslint": "^1.1.0",
46 | "gulp-filter": "^3.0.1",
47 | "gulp-postcss": "^6.1.0",
48 | "gulp-sass": "^2.2.0",
49 | "gulp-stylelint": "^2.0.2",
50 | "history": "^2.0.1",
51 | "html-loader": "^0.4.0",
52 | "html-webpack-plugin": "^2.7.1",
53 | "http-proxy": "^1.12.0",
54 | "json-loader": "^0.5.4",
55 | "node-sass": "^3.4.2",
56 | "opn": "^4.0.0",
57 | "postcss-loader": "^0.8.0",
58 | "react-dom": "^0.14 || ^15.0.0",
59 | "react-router": "^2.0.0",
60 | "react-transform-hmr": "^1.0.1",
61 | "run-sequence": "^1.1.5",
62 | "sass-loader": "^3.1.2",
63 | "style-loader": "^0.13.0",
64 | "stylelint": "^6.5.0",
65 | "stylelint-config-standard": "^8.0.0",
66 | "stylelint-webpack-plugin": "^0.2.0",
67 | "webpack": "^1.12.11",
68 | "webpack-dev-middleware": "^1.2.0",
69 | "webpack-hot-middleware": "^2.5.0",
70 | "webpack-stream": "^3.0.1"
71 | },
72 | "repository": {
73 | "type": "git",
74 | "url": "https://github.com/vn38minhtran/react-tooltip-component.git"
75 | },
76 | "author": "Minh Tran",
77 | "license": "MIT",
78 | "bugs": {
79 | "url": "https://github.com/vn38minhtran/react-tooltip-component/issues"
80 | },
81 | "homepage": "https://github.com/vn38minhtran/react-tooltip-component"
82 | }
83 |
--------------------------------------------------------------------------------
/example/app/components/pages/Home/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Document from 'components/common/Document';
3 | import Tooltip from 'react-tooltip-component';
4 |
5 | class HomePage extends React.Component {
6 | render() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
33 |
36 |
37 |
38 |
41 |
42 |
43 |
46 |
47 |
48 |
49 |
50 |
51 |
54 |
55 |
56 |
59 |
60 |
61 |
64 |
65 |
66 |
69 |
70 |
71 |
72 |
73 | );
74 | }
75 | }
76 |
77 | export default HomePage;
78 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import {spawnSync} from 'child_process';
3 | import del from 'del';
4 | import stylelint from 'gulp-stylelint';
5 | import eslint from 'gulp-eslint';
6 | import babel from 'gulp-babel';
7 | import webpackStream from 'webpack-stream';
8 | import webpackConfig from './webpack.config';
9 | import exampleWebpackConfig from './example/webpack.config';
10 | import webpack from 'webpack';
11 | import sass from 'gulp-sass';
12 | import filter from 'gulp-filter';
13 | import postcss from 'gulp-postcss';
14 | import autoprefixer from 'autoprefixer';
15 | import cssnano from 'cssnano';
16 | import concat from 'gulp-concat';
17 | import pkg from './package.json';
18 | import runSequence from 'run-sequence';
19 |
20 | gulp.task('start', (callback) => {
21 | let start = spawnSync('babel-node', ['example/webpack.server.js'], {stdio: 'inherit'});
22 | if (start.stderr) {
23 | callback(start.stderr);
24 | }
25 | });
26 |
27 | gulp.task('build:lib:clean', () => {
28 | del.sync(['lib', 'dist']);
29 | });
30 |
31 | gulp.task('build:lib:stylelint', () => {
32 | return gulp
33 | .src(['src/**/*.{css,scss,sass}'])
34 | .pipe(stylelint({
35 | failAfterError: true,
36 | reporters: [
37 | {formatter: 'string', console: true}
38 | ]
39 | }));
40 | });
41 |
42 | gulp.task('build:lib:eslint', () => {
43 | return gulp
44 | .src(['src/**/*.js'])
45 | .pipe(eslint())
46 | .pipe(eslint.format())
47 | .pipe(eslint.failOnError());
48 | });
49 |
50 | gulp.task('build:lib:babel', () => {
51 | return gulp
52 | .src(['src/**/*.js'])
53 | .pipe(babel())
54 | .pipe(gulp.dest('lib'));
55 | });
56 |
57 | gulp.task('build:lib:umd', () => {
58 | return gulp
59 | .src(['src/index.js'])
60 | .pipe(webpackStream(webpackConfig, webpack))
61 | .pipe(gulp.dest('dist'));
62 | });
63 |
64 | gulp.task('build:lib:sass', () => {
65 | let cssFilter = filter('**/*.css');
66 | return gulp
67 | .src(['src/**/*.scss', '!src/**/_*.scss'])
68 | .pipe(sass({outputStyle: 'expanded'}).on('error', sass.logError))
69 | .pipe(cssFilter)
70 | .pipe(gulp.dest('lib'))
71 | .pipe(concat(pkg.name + '.css'))
72 | .pipe(postcss([
73 | autoprefixer({
74 | browsers: [
75 | 'ie >= 10',
76 | 'ie_mob >= 10',
77 | 'ff >= 30',
78 | 'chrome >= 34',
79 | 'safari >= 7',
80 | 'opera >= 23',
81 | 'ios >= 7',
82 | 'android >= 4.4',
83 | 'bb >= 10'
84 | ]
85 | }),
86 | cssnano({
87 | safe: true,
88 | discardComments: {removeAll: true}
89 | })
90 | ]))
91 | .pipe(gulp.dest('dist'));
92 | });
93 |
94 | gulp.task('build:lib:copy', () => {
95 | return gulp
96 | .src(['src/**/*', '!src/**/*.{scss,js}'])
97 | .pipe(gulp.dest('lib'))
98 | .pipe(gulp.dest('dist'));
99 | });
100 |
101 | gulp.task('build:lib', (callback) => {
102 | runSequence(
103 | 'build:lib:clean',
104 | 'build:lib:stylelint',
105 | 'build:lib:eslint',
106 | 'build:lib:babel',
107 | 'build:lib:umd',
108 | 'build:lib:sass',
109 | 'build:lib:copy',
110 | callback
111 | );
112 | });
113 |
114 | gulp.task('build:example:clean', () => {
115 | del.sync(['example/dist']);
116 | });
117 |
118 | gulp.task('build:example:webpack', () => {
119 | return gulp
120 | .src(['example/app/app.js'])
121 | .pipe(webpackStream(exampleWebpackConfig, webpack))
122 | .pipe(gulp.dest('example/dist'));
123 | });
124 |
125 | gulp.task('build:example:copy', () => {
126 | return gulp
127 | .src(['example/app/*', '!example/app/*.{html,js}'], {nodir: true})
128 | .pipe(gulp.dest('example/dist'));
129 | });
130 |
131 | gulp.task('build:example', (callback) => {
132 | runSequence(
133 | 'build:example:clean',
134 | 'build:example:webpack',
135 | 'build:example:copy',
136 | callback
137 | );
138 | });
139 |
140 | gulp.task('build', (callback) => {
141 | runSequence('build:lib', 'build:example', callback);
142 | });
143 |
144 | gulp.task('default', ['build']);
145 |
--------------------------------------------------------------------------------
/dist/react-tooltip-component.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react"),require("react-dom")):"function"==typeof define&&define.amd?define(["react","react-dom"],t):"object"==typeof exports?exports.ReactTooltipComponent=t(require("react"),require("react-dom")):e.ReactTooltipComponent=t(e.React,e.ReactDOM)}(this,function(e,t){return function(e){function t(n){if(o[n])return o[n].exports;var r=o[n]={exports:{},id:n,loaded:!1};return e[n].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var o={};return t.m=e,t.c=o,t.p="",t(0)}([function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var r=o(1),i=n(r);t["default"]=i["default"],e.exports=t["default"]},function(e,t,o){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var p=function(){function e(e,t){for(var o=0;o {
105 | let processors = [
106 | autoprefixer({
107 | browsers: [
108 | 'ie >= 10',
109 | 'ie_mob >= 10',
110 | 'ff >= 30',
111 | 'chrome >= 34',
112 | 'safari >= 7',
113 | 'opera >= 23',
114 | 'ios >= 7',
115 | 'android >= 4.4',
116 | 'bb >= 10'
117 | ]
118 | })
119 | ];
120 | if (PROD) {
121 | processors.push(cssnano({
122 | safe: true,
123 | discardComments: {
124 | removeAll: true
125 | }
126 | }));
127 | }
128 | return processors;
129 | },
130 | sassLoader: {
131 | includePaths: [
132 | path.join(__dirname, '../bower_components'),
133 | path.join(__dirname, '../node_modules')
134 | ],
135 | outputStyle: PROD ? 'compressed' : 'expanded'
136 | },
137 | node: {
138 | net: 'mock',
139 | dns: 'mock'
140 | },
141 | debug: DEV,
142 | devtool: DEV ? '#eval' : false,
143 | stats: {
144 | children: false
145 | },
146 | progress: PROD,
147 | profile: PROD,
148 | bail: PROD
149 | };
150 |
151 | if (DEV) {
152 | webpackConfig.plugins = webpackConfig.plugins.concat([
153 | new webpack.HotModuleReplacementPlugin(),
154 | new webpack.NoErrorsPlugin()
155 | ]);
156 | }
157 |
158 | if (PROD) {
159 | webpackConfig.plugins = webpackConfig.plugins.concat([
160 | new ExtractTextPlugin('[contenthash].css'),
161 | new webpack.optimize.UglifyJsPlugin({
162 | sourceMap: false,
163 | compress: {
164 | warnings: false
165 | },
166 | output: {
167 | comments: false
168 | }
169 | }),
170 | new webpack.optimize.DedupePlugin()
171 | ]);
172 | }
173 |
174 | export default webpackConfig;
175 |
--------------------------------------------------------------------------------
/src/Tooltip.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | class Tooltip extends React.Component {
5 | static propTypes = {
6 | container: React.PropTypes.any,
7 | children: React.PropTypes.node.isRequired,
8 | title: React.PropTypes.string.isRequired,
9 | position: React.PropTypes.oneOf(['left', 'top', 'right', 'bottom']),
10 | fixed: React.PropTypes.bool,
11 | space: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number])
12 | };
13 |
14 | static defaultProps = {
15 | container: document.body,
16 | position: 'top',
17 | fixed: true,
18 | space: 5
19 | };
20 |
21 | componentDidMount = () => {
22 | this.container = this.props.container || document.body;
23 | this.componentEl = ReactDOM.findDOMNode(this);
24 | this.tooltipEl = document.createElement('div');
25 |
26 | let tooltipArrowEl = document.createElement('div');
27 | tooltipArrowEl.className = 'tooltip-arrow';
28 |
29 | let tooltipContentEl = document.createElement('div');
30 | tooltipContentEl.className = 'tooltip-inner';
31 | tooltipContentEl.textContent = this.props.title;
32 |
33 | this.tooltipEl.appendChild(tooltipArrowEl);
34 | this.tooltipEl.appendChild(tooltipContentEl);
35 | this.tooltipEl.className = 'tooltip ' + this.props.position;
36 | this.container.appendChild(this.tooltipEl);
37 | this.resetTooltip();
38 |
39 | this.componentEl.addEventListener(this.props.fixed ? 'mouseenter' : 'mousemove', this.handleMouseMove);
40 | this.componentEl.addEventListener('mouseleave', this.handleMouseOut);
41 | };
42 |
43 | componentDidUpdate = () => {
44 | this.tooltipEl.className = 'tooltip ' + this.props.position;
45 | this.tooltipEl.childNodes[1].textContent = this.props.title;
46 | };
47 |
48 |
49 | componentWillUnmount = () => {
50 | this.componentEl.removeEventListener(this.props.fixed ? 'mouseenter' : 'mousemove', this.handleMouseMove);
51 | this.componentEl.removeEventListener('mouseleave', this.handleMouseOut);
52 | this.container.removeChild(this.tooltipEl);
53 | };
54 |
55 | resetTooltip = () => {
56 | this.tooltipEl.style.transition = 'opacity 0.4s';
57 | this.tooltipEl.style.left = '-500px';
58 | this.tooltipEl.style.top = '-500px';
59 | this.tooltipEl.style.opacity = 0;
60 | };
61 |
62 | handleMouseMove = (e) => {
63 | if (this.props.title === '') {
64 | return;
65 | }
66 |
67 | let tooltipPosition = this.getTooltipPosition(e);
68 | let tooltipOffset = this.getTooltipOffset();
69 |
70 | this.tooltipEl.style.left = tooltipPosition.x + tooltipOffset.x + 'px';
71 | this.tooltipEl.style.top = tooltipPosition.y + tooltipOffset.y + 'px';
72 | this.tooltipEl.style.opacity = 1;
73 | };
74 |
75 | handleMouseOut = () => {
76 | this.resetTooltip();
77 | };
78 |
79 | getTooltipPosition = (e) => {
80 | let pointX;
81 | let pointY;
82 | let bodyRect = document.body.getBoundingClientRect();
83 | let containerRect = this.container.getBoundingClientRect();
84 | let containerOffsetX = containerRect.left - bodyRect.left;
85 | let containerOffsetY = containerRect.top - bodyRect.top;
86 | if (this.props.fixed) {
87 | let componentRect = this.componentEl.getBoundingClientRect();
88 | let componentOffsetX = componentRect.left - containerOffsetX;
89 | let componentOffsetY = componentRect.top - containerOffsetY;
90 | let componentWidth = this.componentEl.offsetWidth;
91 | let componentHeight = this.componentEl.offsetHeight;
92 | let cOffsetX = 0;
93 | let cOffsetY = 0;
94 | switch (this.props.position) {
95 | case 'top':
96 | cOffsetX = componentWidth / 2;
97 | cOffsetY = 0;
98 | break;
99 | case 'right':
100 | cOffsetX = componentWidth;
101 | cOffsetY = componentHeight / 2;
102 | break;
103 | case 'bottom':
104 | cOffsetX = componentWidth / 2;
105 | cOffsetY = componentHeight;
106 | break;
107 | case 'left':
108 | cOffsetX = 0;
109 | cOffsetY = componentHeight / 2;
110 | break;
111 | }
112 | pointX = componentOffsetX + cOffsetX + (window.scrollX || window.pageXOffset);
113 | pointY = componentOffsetY + cOffsetY + (window.scrollY || window.pageYOffset);
114 | } else {
115 | let clientX = e.clientX;
116 | let clientY = e.clientY;
117 | pointX = clientX - containerOffsetX + (window.scrollX || window.pageXOffset);
118 | pointY = clientY - containerOffsetY + (window.scrollY || window.pageYOffset);
119 | }
120 | return {
121 | x: pointX,
122 | y: pointY
123 | };
124 | };
125 |
126 | getTooltipOffset = () => {
127 | let tooltipW = this.tooltipEl.offsetWidth;
128 | let tooltipH = this.tooltipEl.offsetHeight;
129 | let offsetX = 0;
130 | let offsetY = 0;
131 | switch (this.props.position) {
132 | case 'top':
133 | offsetX = -(tooltipW / 2);
134 | offsetY = -(tooltipH + Number(this.props.space));
135 | break;
136 | case 'right':
137 | offsetX = Number(this.props.space);
138 | offsetY = -(tooltipH / 2);
139 | break;
140 | case 'bottom':
141 | offsetX = -(tooltipW / 2);
142 | offsetY = Number(this.props.space);
143 | break;
144 | case 'left':
145 | offsetX = -(tooltipW + Number(this.props.space));
146 | offsetY = -(tooltipH / 2);
147 | break;
148 | }
149 | return {
150 | x: offsetX,
151 | y: offsetY
152 | };
153 | };
154 |
155 | render() {
156 | return this.props.children;
157 | }
158 | }
159 |
160 | export default Tooltip;
161 |
--------------------------------------------------------------------------------
/lib/Tooltip.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', {
4 | value: true
5 | });
6 |
7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
8 |
9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12 |
13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14 |
15 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _reactDom = require('react-dom');
22 |
23 | var _reactDom2 = _interopRequireDefault(_reactDom);
24 |
25 | var Tooltip = (function (_React$Component) {
26 | _inherits(Tooltip, _React$Component);
27 |
28 | function Tooltip() {
29 | var _this = this;
30 |
31 | _classCallCheck(this, Tooltip);
32 |
33 | _get(Object.getPrototypeOf(Tooltip.prototype), 'constructor', this).apply(this, arguments);
34 |
35 | this.componentDidMount = function () {
36 | _this.container = _this.props.container || document.body;
37 | _this.componentEl = _reactDom2['default'].findDOMNode(_this);
38 | _this.tooltipEl = document.createElement('div');
39 |
40 | var tooltipArrowEl = document.createElement('div');
41 | tooltipArrowEl.className = 'tooltip-arrow';
42 |
43 | var tooltipContentEl = document.createElement('div');
44 | tooltipContentEl.className = 'tooltip-inner';
45 | tooltipContentEl.textContent = _this.props.title;
46 |
47 | _this.tooltipEl.appendChild(tooltipArrowEl);
48 | _this.tooltipEl.appendChild(tooltipContentEl);
49 | _this.tooltipEl.className = 'tooltip ' + _this.props.position;
50 | _this.container.appendChild(_this.tooltipEl);
51 | _this.resetTooltip();
52 |
53 | _this.componentEl.addEventListener(_this.props.fixed ? 'mouseenter' : 'mousemove', _this.handleMouseMove);
54 | _this.componentEl.addEventListener('mouseleave', _this.handleMouseOut);
55 | };
56 |
57 | this.componentDidUpdate = function () {
58 | _this.tooltipEl.className = 'tooltip ' + _this.props.position;
59 | _this.tooltipEl.childNodes[1].textContent = _this.props.title;
60 | };
61 |
62 | this.componentWillUnmount = function () {
63 | _this.componentEl.removeEventListener(_this.props.fixed ? 'mouseenter' : 'mousemove', _this.handleMouseMove);
64 | _this.componentEl.removeEventListener('mouseleave', _this.handleMouseOut);
65 | _this.container.removeChild(_this.tooltipEl);
66 | };
67 |
68 | this.resetTooltip = function () {
69 | _this.tooltipEl.style.transition = 'opacity 0.4s';
70 | _this.tooltipEl.style.left = '-500px';
71 | _this.tooltipEl.style.top = '-500px';
72 | _this.tooltipEl.style.opacity = 0;
73 | };
74 |
75 | this.handleMouseMove = function (e) {
76 | if (_this.props.title === '') {
77 | return;
78 | }
79 |
80 | var tooltipPosition = _this.getTooltipPosition(e);
81 | var tooltipOffset = _this.getTooltipOffset();
82 |
83 | _this.tooltipEl.style.left = tooltipPosition.x + tooltipOffset.x + 'px';
84 | _this.tooltipEl.style.top = tooltipPosition.y + tooltipOffset.y + 'px';
85 | _this.tooltipEl.style.opacity = 1;
86 | };
87 |
88 | this.handleMouseOut = function () {
89 | _this.resetTooltip();
90 | };
91 |
92 | this.getTooltipPosition = function (e) {
93 | var pointX = undefined;
94 | var pointY = undefined;
95 | var bodyRect = document.body.getBoundingClientRect();
96 | var containerRect = _this.container.getBoundingClientRect();
97 | var containerOffsetX = containerRect.left - bodyRect.left;
98 | var containerOffsetY = containerRect.top - bodyRect.top;
99 | if (_this.props.fixed) {
100 | var componentRect = _this.componentEl.getBoundingClientRect();
101 | var componentOffsetX = componentRect.left - containerOffsetX;
102 | var componentOffsetY = componentRect.top - containerOffsetY;
103 | var componentWidth = _this.componentEl.offsetWidth;
104 | var componentHeight = _this.componentEl.offsetHeight;
105 | var cOffsetX = 0;
106 | var cOffsetY = 0;
107 | switch (_this.props.position) {
108 | case 'top':
109 | cOffsetX = componentWidth / 2;
110 | cOffsetY = 0;
111 | break;
112 | case 'right':
113 | cOffsetX = componentWidth;
114 | cOffsetY = componentHeight / 2;
115 | break;
116 | case 'bottom':
117 | cOffsetX = componentWidth / 2;
118 | cOffsetY = componentHeight;
119 | break;
120 | case 'left':
121 | cOffsetX = 0;
122 | cOffsetY = componentHeight / 2;
123 | break;
124 | }
125 | pointX = componentOffsetX + cOffsetX + (window.scrollX || window.pageXOffset);
126 | pointY = componentOffsetY + cOffsetY + (window.scrollY || window.pageYOffset);
127 | } else {
128 | var clientX = e.clientX;
129 | var clientY = e.clientY;
130 | pointX = clientX - containerOffsetX + (window.scrollX || window.pageXOffset);
131 | pointY = clientY - containerOffsetY + (window.scrollY || window.pageYOffset);
132 | }
133 | return {
134 | x: pointX,
135 | y: pointY
136 | };
137 | };
138 |
139 | this.getTooltipOffset = function () {
140 | var tooltipW = _this.tooltipEl.offsetWidth;
141 | var tooltipH = _this.tooltipEl.offsetHeight;
142 | var offsetX = 0;
143 | var offsetY = 0;
144 | switch (_this.props.position) {
145 | case 'top':
146 | offsetX = -(tooltipW / 2);
147 | offsetY = -(tooltipH + Number(_this.props.space));
148 | break;
149 | case 'right':
150 | offsetX = Number(_this.props.space);
151 | offsetY = -(tooltipH / 2);
152 | break;
153 | case 'bottom':
154 | offsetX = -(tooltipW / 2);
155 | offsetY = Number(_this.props.space);
156 | break;
157 | case 'left':
158 | offsetX = -(tooltipW + Number(_this.props.space));
159 | offsetY = -(tooltipH / 2);
160 | break;
161 | }
162 | return {
163 | x: offsetX,
164 | y: offsetY
165 | };
166 | };
167 | }
168 |
169 | _createClass(Tooltip, [{
170 | key: 'render',
171 | value: function render() {
172 | return this.props.children;
173 | }
174 | }], [{
175 | key: 'propTypes',
176 | value: {
177 | container: _react2['default'].PropTypes.any,
178 | children: _react2['default'].PropTypes.node.isRequired,
179 | title: _react2['default'].PropTypes.string.isRequired,
180 | position: _react2['default'].PropTypes.oneOf(['left', 'top', 'right', 'bottom']),
181 | fixed: _react2['default'].PropTypes.bool,
182 | space: _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.string, _react2['default'].PropTypes.number])
183 | },
184 | enumerable: true
185 | }, {
186 | key: 'defaultProps',
187 | value: {
188 | container: document.body,
189 | position: 'top',
190 | fixed: true,
191 | space: 5
192 | },
193 | enumerable: true
194 | }]);
195 |
196 | return Tooltip;
197 | })(_react2['default'].Component);
198 |
199 | exports['default'] = Tooltip;
200 | module.exports = exports['default'];
--------------------------------------------------------------------------------