├── .babelrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __tests__ ├── Tooltip.test.jsx └── tooltip.puppeteer.test.jsx ├── docs ├── 5766131523f20ee7c976741d51726742.svg ├── 7cdd37002ea0abdc81a186979cabf404.svg ├── 9bf3efb595d2a163db7914c8e111957b.svg ├── bundle.js ├── favicon.png └── index.html ├── jest.setup.js ├── lib ├── Tooltip │ ├── Arrow.js │ ├── TextBox.js │ └── styles.js └── index.js ├── package-lock.json ├── package.json ├── postinstall.js ├── puppeteer ├── bundle.js ├── index.html └── input │ ├── index.html │ └── index.jsx ├── src ├── assets │ ├── arrow-down.svg │ ├── arrow-up.svg │ ├── favicon.png │ └── logo.svg ├── docs │ ├── Content │ │ ├── AdvancedUse.jsx │ │ ├── Api.jsx │ │ ├── BasicUse.jsx │ │ ├── CodeSnippets.jsx │ │ ├── Examples │ │ │ ├── Animation │ │ │ │ ├── Bounce.jsx │ │ │ │ ├── FadeIn.jsx │ │ │ │ ├── SlideIn.jsx │ │ │ │ └── index.jsx │ │ │ ├── Customization │ │ │ │ ├── Colors.jsx │ │ │ │ └── Shapes.jsx │ │ │ ├── Positions │ │ │ │ ├── AdjPosIn.jsx │ │ │ │ ├── AdjPosOut.jsx │ │ │ │ ├── Align.jsx │ │ │ │ ├── ArrowHPos.jsx │ │ │ │ └── ArrowVPos.jsx │ │ │ ├── Types │ │ │ │ ├── Alert.jsx │ │ │ │ └── Types.jsx │ │ │ └── index.jsx │ │ ├── Header.jsx │ │ ├── Intro.jsx │ │ ├── SideNav.jsx │ │ └── ToggleCode.jsx │ ├── index.html │ ├── index.jsx │ └── styles.css └── lib │ ├── Tooltip │ ├── Arrow.jsx │ ├── TextBox.jsx │ └── styles.jsx │ └── index.jsx └── webpack ├── dev.config.js ├── prod.config.js └── test.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env", 4 | "@babel/react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-object-rest-spread", 8 | "@babel/plugin-proposal-class-properties", 9 | [ 10 | "prismjs", 11 | { 12 | "languages": [ 13 | "javascript", 14 | "css", 15 | "markup", 16 | "jsx" 17 | ], 18 | "plugins": [ 19 | "line-numbers" 20 | ], 21 | "theme": "default", 22 | "css": true 23 | } 24 | ] 25 | ] 26 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | /docs 3 | /puppeteer/bundle.js 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest": true 6 | }, 7 | "extends": [ 8 | "airbnb" 9 | ], 10 | "parser": "babel-eslint", 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "sourceType": "module" 16 | }, 17 | "rules": { 18 | "comma-dangle": [ 19 | "warn", 20 | "never" 21 | ], 22 | "object-curly-spacing": "off", 23 | "object-curly-newline": "off", 24 | "arrow-body-style": "off", 25 | "no-nested-ternary": "off", 26 | "no-confusing-arrow": "off", 27 | "react/prop-types": "off", 28 | "react/destructuring-assignment": "off", 29 | "jsx-a11y/mouse-events-have-key-events": "off", 30 | "jsx-a11y/click-events-have-key-events": "off", 31 | "import/no-extraneous-dependencies": "off" 32 | } 33 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | puppeteer 3 | docs 4 | src 5 | webpack 6 | .babelrc 7 | .eslintignore 8 | .eslintrc 9 | .gitignore 10 | .travis.yml 11 | CHANGELOG.md 12 | CONTRIBUTING.md 13 | jest.setup.js 14 | README.md 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "11.10.1" 5 | 6 | dist: trusty 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | install: 13 | - npm install 14 | 15 | before_script: 16 | - npm run mock:create 17 | 18 | script: 19 | - npm run test 20 | - npm run lint -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | 9 | ## [1.1.0] - YYYY-MM-DD 10 | 11 | #### Added (n/a) 12 | #### Removed (n/a) 13 | #### Changed (n/a) 14 | #### Refactored (n/a) 15 | #### Fixed (n/a) 16 | #### Deprecated (n/a) 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to React-power-tooltip 2 | 3 | First off, thank you for considering contributing to React-power-tooltip. This project is open source and relies on people like you for support. 4 | 5 | ### 1. Where do I go from here? 6 | 7 | If you've noticed a bug or have a question that doesn't belong on [Stack Overflow][], [search the issue tracker][] to see if someone else in the community has already created a ticket. If not, go ahead and [make one][new issue]! 8 | 9 | ### 2. Fork & create a branch 10 | 11 | If this is something you think you can fix, then [fork React-power-tooltip][] and 12 | create a branch with a descriptive name. 13 | 14 | A good branch name would be (where issue #325 is the ticket you're working on): 15 | 16 | ```sh 17 | git checkout -b 325-add-mount-animations 18 | ``` 19 | 20 | ### 3. Get the test suite running 21 | 22 | Make sure you're have a recent **npm & node.js** version installed. 23 | 24 | Now install the development dependencies in the project's root folder: 25 | 26 | ```sh 27 | npm install 28 | ``` 29 | Once all dependencies are installed, you can use the test suite by running: 30 | 31 | ```sh 32 | npm run test 33 | ``` 34 | and for automatic test runs on file updates: 35 | 36 | ```sh 37 | npm run test:watch 38 | ``` 39 | **NOTE:** If your tests are passing locally but failing on Travis, check your development environment (i.e. nodejs version) and dependencies. 40 | 41 | ### 4. You have found a bug? 42 | 43 | * **Ensure the bug was not already reported** by [searching all issues][]. 44 | * If you're unable to find an open issue addressing the problem, 45 | [open a new one][new issue]. Be sure to include a **title and clear 46 | description**, as much relevant information as possible, and a **code sample** 47 | or an **executable test case** demonstrating the expected behavior that is not 48 | occurring. 49 | 50 | ### 5. Implement your fix or feature 51 | 52 | At this point, you're ready to make your changes! Feel free to ask for help; 53 | everyone is a beginner at first :thumbsup: 54 | 55 | ### 6. View your changes in your browser 56 | 57 | Besides running tests _before_ and _after_ your changes you should also always check your changes in the browser. This ensures the user experience is maintained. 58 | 59 | To see your changes hot-loaded in the browser run: 60 | 61 | ```sh 62 | npm run dev 63 | ``` 64 | 65 | This will boot up a webpack development server. 66 | 67 | You should now be able to open the project's documentation page in your browser on . 68 | 69 | Changes to the library will be immediately visible (hot-loaded) in the examples of the documentation page. 70 | 71 | ### 7. Get the style right 72 | 73 | Your patch should follow the same conventions & pass the same code quality checks as the rest of the project. 74 | The project uses airbnb's code style convention. To check that your code adheres to this style convention run: 75 | 76 | ```sh 77 | npm run lint 78 | ``` 79 | Warnings should indicate where changes in your code need to be made. 80 | 81 | ### 8. Make a Pull Request 82 | 83 | At this point, you should switch back to your master branch and make sure it's 84 | up to date with React-power-tooltip's master branch: 85 | 86 | ```sh 87 | git remote add upstream git@github.com:react-power-tooltip/react-power-tooltip.git 88 | git checkout master 89 | git pull upstream master 90 | ``` 91 | 92 | Then update your feature branch from your local copy of master, and push it! 93 | 94 | ```sh 95 | git checkout 325-add-mount-animation 96 | git rebase master 97 | git push --set-upstream origin 325-add-mount-animation 98 | ``` 99 | 100 | Finally, go to GitHub and [make a Pull Request][] :thumbsup: 101 | 102 | TravisCI will run our test suite against your code. We care about quality, so your PR 103 | won't be merged until all tests pass. It's unlikely, but it's possible that your changes 104 | pass tests in one nodejs version but fail in another. In that case, you'll have to setup 105 | your development environment (as explained in step 3) to use the problematic node version, 106 | and investigate what's going on! 107 | 108 | ### 8. Keeping your Pull Request updated 109 | 110 | If a maintainer asks you to "rebase" your PR, they're saying that a lot of code 111 | has changed, and that you need to update your branch so it's easier to merge. 112 | 113 | To learn more about rebasing in Git, there are a lot of [good][git rebasing] 114 | [resources][interactive rebase] but here's the suggested workflow: 115 | 116 | ```sh 117 | git checkout 325-add-mount-animation 118 | git pull --rebase upstream master 119 | git push --force-with-lease 325-add-mount-animation 120 | ``` 121 | 122 | ### 10. Merging a PR (maintainers only) 123 | 124 | A PR can only be merged into master by a maintainer if: 125 | 126 | * It is passing CI. 127 | * It has been approved by at least two maintainers. If it was a maintainer who 128 | opened the PR, only one extra approval is needed. 129 | * It has no requested changes. 130 | * It is up to date with current master. 131 | 132 | Any maintainer is allowed to merge a PR if all of these conditions are 133 | met. 134 | 135 | ### 11. Shipping a release (maintainers only) 136 | 137 | Maintainers need to do the following to push out a release: 138 | 139 | * Make sure all pull requests are in and that changelog is current 140 | * Update the changelog with the new version number 141 | * Create a stable branch for that release: 142 | 143 | ```sh 144 | git checkout master 145 | git fetch react-power-tooltip 146 | git rebase react-power-tooltip/master 147 | # If the release is 2.1.x then this should be: 2-1-stable 148 | git checkout -b N-N-stable 149 | git push react-power-tooltip N-N-stable:N-N-stable 150 | ``` 151 | 152 | [Stack Overflow]: http://stackoverflow.com/questions/tagged/react-power-tooltip 153 | [search the issue tracker]: https://github.com/justinrhodes1/react-power-tooltip/issues?q=something 154 | [new issue]: https://github.com/justinrhodes1/react-power-tooltip/issues/new 155 | [fork React-power-tootlip]: https://help.github.com/articles/fork-a-repo 156 | [searching all issues]: https://github.com/justinrhodes1/react-power-tooltip/issues?q= 157 | [make a pull request]: https://help.github.com/articles/creating-a-pull-request 158 | [git rebasing]: http://git-scm.com/book/en/Git-Branching-Rebasing 159 | [interactive rebase]: https://help.github.com/articles/interactive-rebase -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Justin Rhodes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-power-tooltip 2 | 3 | ![Travis (.org)](https://img.shields.io/travis/justinrhodes1/react-power-tooltip.svg) 4 | ![Coveralls github branch](https://img.shields.io/coveralls/github/justinrhodes1/react-power-tooltip/master.svg) ![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/react-power-tooltip.svg) 5 | 6 | 7 | 8 | A **powerful** and **elegant** alternative for all your tooltips and menu needs. 9 | 10 | - **Different Types** - For every use context: Choose between _Hoverable_, _Static_ & _Alert_ tooltips. 11 | - **Fully Customizable** - Easily change default settings via props 12 | - **Reliable Positioning** - Align your tooltip to your 13 | target element with ease 14 | - **Advanced Customization** - Add your own animations and styles via separate CSS files 15 | 16 | ## DEMO 17 | 18 | Check out the [documentation & demo](https://justinrhodes1.github.io/react-power-tooltip/) pages to see all use cases. 19 | 20 | ## Installation 21 | 22 | ### NPM 23 | 24 | ```bash 25 | npm install react-power-tooltip 26 | ``` 27 | 28 | ## Usage 29 | 30 | **Important**: Set the position of the hoverable parent component to *relative*. 31 | 32 | ```jsx 33 | import React, { Component } from "react"; 34 | import Tooltip from "react-power-tooltip"; 35 | 36 | class Example extends Component { 37 | state = { 38 | show: false 39 | } 40 | 41 | showTooltip = bool => { 42 | this.setState({ show: bool }) 43 | } 44 | render() { 45 | return ( 46 |
this.showTooltip(true)} 49 | onMouseLeave={() => this.showTooltip(false)} 50 | > 51 | 52 | Option 1 53 | Option 2 54 | 55 |
56 | ); 57 | } 58 | } 59 | export default Example; 60 | ``` 61 | ## API 62 | 63 | | Props | Types / Options | Default | Description | 64 | | --------------- | --------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------- | 65 | | show | bool: false, true | false | Mount tooltip if true. | 66 | | fontFamily | string: font family | 'inherit' | Font family of text | 67 | | fontSize | string: px | 'inherit' | Font size of text | 68 | | fontWeight | string | 'bold' | Font weight of text | 69 | | color | string | 'inherit' | Font color of text | 70 | | animation | string: fade _or_ bounce | 'fade' | Mount/Unmount anmation. Custom animations: See advanced usage examples. | 71 | | hoverBackground | string: hex colors | '#ececec' | Background color on hover | 72 | | hoverColor | string: hex colors | '#000000' | Font color on hover | 73 | | backgroundColor | string: hex colors | '#ffffff' | Background color | 74 | | alert | string: rgb colors | false | Pulse animation | 75 | | textBoxWidth | string: px _or_ auto | '150px' | Width of the text box | 76 | | padding | string: px | '15px 20px' | Padding of text | 77 | | borderRadius | string: px | '5px' | Radius of corners | 78 | | zIndex | string: number | '100' | Z-index of tooltip | 79 | | moveDown | string: px | '0px' | Downward position adjustment | 80 | | moveRight | string: px | '0px' | Right position adjustment | 81 | | static | boolean: false _or_ true | false | Disable hover animations | 82 | | flat | boolean: false _or_ true | false | Disable shadows | 83 | | lineSeparated | boolean: false _or_ true _or_ string: css border property | '1px solid #ececec' | Enable ∓ specify line separation between options | 84 | | arrowAlign | string: 'start' _or_ 'center' _or_ 'end' | 'start' | Positions arrow relative to textbox | 85 | | position | string: 'position1 position2' | 'right center' | Positions tooltip relative to target element | 86 | 87 | 88 | 89 | ## Development 90 | 91 | You're welcome to contribute to react-power-tooltip. 92 | 93 | To set up the project: 94 | 95 | 1. Fork and clone the repository 96 | 2. `$ npm install` 97 | 3. `$ npm run dev` 98 | 99 | The demo page will then be served on http://localhost:8000/ in watch mode, meaning you don't have refresh the page to see your changes. 100 | 101 | ## Contributors 102 | 103 | 104 | 105 | 106 | 111 | 112 | 113 |
107 | 108 |
109 | Justin Rhodes 110 |
114 | 115 | ## License 116 | 117 | MIT -------------------------------------------------------------------------------- /__tests__/Tooltip.test.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow, mount } from 'enzyme'; 3 | 4 | import Tooltip from '../src/lib'; 5 | import ArrowComp from '../src/lib/Tooltip/Arrow'; 6 | import TextBoxComp from '../src/lib/Tooltip/TextBox'; 7 | 8 | // Helper functions 9 | 10 | const numClassesOf = (el) => { 11 | return el.prop('className').split(' ').length; 12 | }; 13 | 14 | const setPropsAndgetRctContainer = (wrapper, str) => { 15 | wrapper.setProps({ position: str }); 16 | return wrapper.find('.rpt-container'); 17 | }; 18 | 19 | 20 | // TEST SETUP 21 | let wrapper; 22 | let span; 23 | let arrow; 24 | let shadowContainer; 25 | 26 | const singleTooltip = ( 27 | 28 | Option 1 29 | 30 | ); 31 | 32 | const hiddenTooltip = ( 33 | 34 | Option 1 35 | 36 | ); 37 | 38 | const multipleTooltip = ( 39 | 40 | Option 1 41 | Option 2 42 | Option 3 43 | 44 | ); 45 | 46 | const defaultSettings = { 47 | backgroundColor: 'white', 48 | hoverBackground: '#ececec', 49 | color: 'black', 50 | hoverColor: 'black', 51 | padding: '15px 20px', 52 | textAlign: 'left', 53 | fontWeight: 'bold', 54 | arwShadow: '0 0 0 1px rgba(0,0,0,.18)' 55 | }; 56 | 57 | const props = { 58 | backgroundColor: 'grey', 59 | color: 'blue', 60 | padding: '50px 60px', 61 | textAlign: 'center', 62 | fontWeight: 'normal', 63 | borderRadius: '0px', 64 | lineSeparated: '2px solid #dddddd' 65 | }; 66 | 67 | // TODO: shallow render components & test individually 68 | // to avoid side-effects 69 | 70 | describe('SHALLOW component testing', () => { 71 | describe('DEFAULT SINGLE', () => { 72 | beforeEach(() => { 73 | wrapper = shallow(singleTooltip); 74 | }); 75 | it('renders arrow', () => { 76 | expect(wrapper.find(ArrowComp).length).toEqual(1); 77 | }); 78 | it('renders textBox', () => { 79 | expect(wrapper.find(TextBoxComp).length).toEqual(1); 80 | }); 81 | }); 82 | 83 | describe('DEFAULT MULTIPLE', () => { 84 | beforeEach(() => { 85 | wrapper = shallow(multipleTooltip); 86 | }); 87 | it('renders arrow', () => { 88 | expect(wrapper.find(ArrowComp).length).toEqual(1); 89 | }); 90 | it('renders textBox', () => { 91 | expect(wrapper.find(TextBoxComp).length).toEqual(1); 92 | }); 93 | }); 94 | }); 95 | 96 | describe('DEEP component testing', () => { 97 | afterEach(() => { 98 | wrapper.unmount(); 99 | }); 100 | describe('SINGLE TOOLTIP', () => { 101 | beforeEach(() => { 102 | wrapper = mount(singleTooltip); 103 | span = wrapper.find('span'); 104 | arrow = wrapper.find('.rpt-arrow'); 105 | }); 106 | 107 | it('renders no tooltip if show prop = false', () => { 108 | expect(mount(hiddenTooltip) 109 | .children().length).toEqual(0); 110 | }); 111 | 112 | it('renders span', () => { 113 | expect(span.length).toEqual(1); 114 | }); 115 | 116 | it('renders correct span style', () => { 117 | const { backgroundColor, padding } = defaultSettings; 118 | expect(span).toHaveStyle('padding', padding); 119 | expect(span).toHaveStyle('backgroundColor', backgroundColor); 120 | }); 121 | 122 | it('renders correct custom span style', () => { 123 | wrapper.setProps(props); 124 | span = wrapper.find('span'); 125 | const { backgroundColor, padding } = props; 126 | expect(span).toHaveStyle('padding', padding); 127 | expect(span).toHaveStyle('backgroundColor', backgroundColor); 128 | }); 129 | 130 | it('renders correct arrow style', () => { 131 | const { backgroundColor, arwShadow } = defaultSettings; 132 | expect(arrow).toHaveStyle('backgroundColor', backgroundColor); 133 | expect(arrow).toHaveStyle('boxShadow', arwShadow); 134 | }); 135 | 136 | it('renders correct hover styles', () => { 137 | const { hoverBackground, hoverColor } = defaultSettings; 138 | // component updates before tests 139 | span.simulate('mouseover'); 140 | wrapper.update(); 141 | const updatedSpan = wrapper.find('span'); 142 | expect(updatedSpan).toHaveStyle('backgroundColor', hoverBackground); 143 | expect(updatedSpan).toHaveStyle('color', hoverColor); 144 | const updatedArrow = wrapper.find('.rpt-arrow'); 145 | expect(updatedArrow).toHaveStyle('backgroundColor', hoverBackground); 146 | expect(updatedSpan.parent().hasClass('rpt-hover')).toEqual(true); 147 | }); 148 | 149 | it('renders correct arrow style when textbox unhovered', () => { 150 | const { backgroundColor } = defaultSettings; 151 | // component updates before tests 152 | span.simulate('mouseover'); 153 | wrapper.update(); 154 | wrapper.find('.rpt-textbox').simulate('mouseleave'); 155 | wrapper.update(); 156 | const updatedArrow = wrapper.find('.rpt-arrow'); 157 | expect(updatedArrow).toHaveStyle('backgroundColor', backgroundColor); 158 | }); 159 | 160 | it('renders as static (no hover effects) if static prop = true', () => { 161 | const { backgroundColor } = defaultSettings; 162 | wrapper.setProps({ static: true }); 163 | // hover & component updates before tests 164 | span.simulate('mouseover'); 165 | wrapper.update(); 166 | const updatedSpan = wrapper.find('span'); 167 | expect(updatedSpan).toHaveStyle('backgroundColor', backgroundColor); 168 | expect(updatedSpan.parent().hasClass('rpt-hover')).toEqual(false); 169 | }); 170 | 171 | it('renders no shadows if flat prop = true', () => { 172 | wrapper.setProps({ flat: true }); 173 | arrow = wrapper.find('.rpt-arrow'); 174 | expect(arrow).toHaveStyle('boxShadow', null); 175 | shadowContainer = wrapper.find('.rpt-shadow-container'); 176 | expect(shadowContainer).toHaveStyle('boxShadow', null); 177 | }); 178 | 179 | it('renders correct tooltip position: right top', () => { 180 | wrapper.setProps({ position: 'right top' }); 181 | const rctContainer = wrapper.find('.rpt-container'); 182 | expect(rctContainer.hasClass('rpt-right')).toEqual(true); 183 | expect(numClassesOf(rctContainer)).toEqual(2); 184 | }); 185 | 186 | it('renders correct tooltip position: right center', () => { 187 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'right center'); 188 | expect(rctContainer.hasClass('rpt-right rpt-align-center')).toEqual(true); 189 | expect(numClassesOf(rctContainer)).toEqual(3); 190 | }); 191 | 192 | it('renders correct tooltip position: right bottom', () => { 193 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'right bottom'); 194 | expect(rctContainer.hasClass('rpt-right rpt-align-bottom')).toEqual(true); 195 | expect(numClassesOf(rctContainer)).toEqual(3); 196 | }); 197 | 198 | it('renders correct tooltip position: left top', () => { 199 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'left top'); 200 | expect(rctContainer.hasClass('rpt-left')).toEqual(true); 201 | expect(numClassesOf(rctContainer)).toEqual(2); 202 | }); 203 | 204 | it('renders correct tooltip position: left center', () => { 205 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'left center'); 206 | expect(rctContainer.hasClass('rpt-left rpt-align-center')).toEqual(true); 207 | expect(numClassesOf(rctContainer)).toEqual(3); 208 | }); 209 | 210 | it('renders correct tooltip position: left bottom', () => { 211 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'left bottom'); 212 | expect(rctContainer.hasClass('rpt-left rpt-align-bottom')).toEqual(true); 213 | expect(numClassesOf(rctContainer)).toEqual(3); 214 | }); 215 | 216 | it('renders correct tooltip position: top left', () => { 217 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'top left'); 218 | expect(rctContainer.hasClass('rpt-top rpt-align-left')).toEqual(true); 219 | expect(numClassesOf(rctContainer)).toEqual(3); 220 | }); 221 | 222 | it('renders correct tooltip position: top center', () => { 223 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'top center'); 224 | expect(rctContainer.hasClass('rpt-top')).toEqual(true); 225 | expect(numClassesOf(rctContainer)).toEqual(2); 226 | }); 227 | 228 | it('renders correct tooltip position: top right', () => { 229 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'top right'); 230 | expect(rctContainer.hasClass('rpt-top rpt-align-right')).toEqual(true); 231 | expect(numClassesOf(rctContainer)).toEqual(3); 232 | }); 233 | 234 | it('renders correct tooltip position: bottom left', () => { 235 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'bottom left'); 236 | expect(rctContainer.hasClass('rpt-bottom rpt-align-left')).toEqual(true); 237 | expect(numClassesOf(rctContainer)).toEqual(3); 238 | }); 239 | 240 | it('renders correct tooltip position: bottom center', () => { 241 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'bottom center'); 242 | expect(rctContainer.hasClass('rpt-bottom')).toEqual(true); 243 | expect(numClassesOf(rctContainer)).toEqual(2); 244 | }); 245 | 246 | it('renders correct tooltip position: bottom right', () => { 247 | const rctContainer = setPropsAndgetRctContainer(wrapper, 'bottom right'); 248 | expect(rctContainer.hasClass('rpt-bottom rpt-align-right')).toEqual(true); 249 | expect(numClassesOf(rctContainer)).toEqual(3); 250 | }); 251 | 252 | it('renders correct horizontal arrow position: left', () => { 253 | wrapper.setProps({ arrow: 'left', position: 'bottom center' }); 254 | const rctTxtbxContainer = wrapper.find('.rpt-textbox-container'); 255 | expect(rctTxtbxContainer).toHaveStyle('left', 'calc(50% - 40px)'); 256 | }); 257 | 258 | it('renders correct horizontal arrow position: center', () => { 259 | wrapper.setProps({ arrow: 'center', position: 'bottom center' }); 260 | const rctTxtbxContainer = wrapper.find('.rpt-textbox-container'); 261 | expect(rctTxtbxContainer).toHaveStyle('left', 'calc(50% - 40px)'); 262 | }); 263 | 264 | it('renders correct horizontal arrow position: right', () => { 265 | wrapper.setProps({ arrow: 'right', position: 'bottom center' }); 266 | const rctTxtbxContainer = wrapper.find('.rpt-textbox-container'); 267 | expect(rctTxtbxContainer).toHaveStyle('right', ''); 268 | }); 269 | }); 270 | 271 | describe('MULTIPLE OPTION TOOLTIP', () => { 272 | beforeEach(() => { 273 | wrapper = mount(multipleTooltip); 274 | span = wrapper.find('span'); 275 | }); 276 | 277 | it('renders correct amount of spans', () => { 278 | expect(span.length).toEqual(3); 279 | }); 280 | }); 281 | }); 282 | -------------------------------------------------------------------------------- /__tests__/tooltip.puppeteer.test.jsx: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | const path = require('path'); 3 | 4 | const filePath = path.join(__dirname, '/../puppeteer/index.html'); 5 | const URL = `file:///${filePath}`; 6 | let browser; 7 | let page; 8 | 9 | // Pupeteer/Chromium tests required to test computed styles & 10 | // those relying on clientHeight etc. This is beyond Enyzme/JSDOM. 11 | 12 | // Allowing enough time for puppeteer to run 13 | jest.setTimeout(30000); 14 | 15 | beforeEach(async () => { 16 | // both options below not needed, switching off speeds things up 17 | browser = await puppeteer.launch({ 18 | headless: true, 19 | args: ['--no-sandbox'] 20 | }); 21 | page = await browser.newPage(); 22 | await page.goto(URL); 23 | }); 24 | 25 | afterEach(async () => { 26 | await browser.close(); 27 | }); 28 | 29 | it('renders correct textbox positions for arrow positions', async () => { 30 | const styleTop = await page 31 | .$eval('.test-arrow-top .rpt-textbox-container', el => el.style.top); 32 | const styleCenter = await page 33 | .$eval('.test-arrow-center .rpt-textbox-container', el => el.style.top); 34 | const styleBottom = await page 35 | .$eval('.test-arrow-bottom .rpt-textbox-container', el => el.style.top); 36 | 37 | expect(styleTop).toEqual('calc(((50% - 0px) - 24px) + 0px)'); 38 | expect(styleCenter).toEqual('calc(50% - 72px)'); 39 | expect(styleBottom).toEqual('calc(((50% - 144px) - -24px) + 0px)'); 40 | }); 41 | -------------------------------------------------------------------------------- /docs/5766131523f20ee7c976741d51726742.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 48 | 52 | 53 | 54 | 72 | 77 | 78 | 80 | 81 | 83 | image/svg+xml 84 | 86 | 87 | 88 | 89 | 90 | 95 | 100 | 103 | 109 | 110 | 113 | 118 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /docs/7cdd37002ea0abdc81a186979cabf404.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/9bf3efb595d2a163db7914c8e111957b.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinrhodes1/react-power-tooltip/fb2df8120e0d64ed81c53fcaaef845157f1a9e0c/docs/favicon.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React-power-tooltip | The ultimate tooltip library 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /jest.setup.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | import 'jest-enzyme'; 4 | 5 | // Setup enzyme's react adapter 6 | Enzyme.configure({ adapter: new Adapter() }); 7 | -------------------------------------------------------------------------------- /lib/Tooltip/Arrow.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireDefault(require("react")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 11 | 12 | // 13 | var Arrow = function Arrow(_ref) { 14 | var isHovered = _ref.isHovered, 15 | hovBkg = _ref.hovBkg, 16 | bkgCol = _ref.bkgCol, 17 | flat = _ref.flat; 18 | var backgroundColor = isHovered ? hovBkg : bkgCol; 19 | var boxShadow = flat ? null : '0 0 0 1px rgba(0,0,0,.18)'; 20 | return _react.default.createElement("div", { 21 | className: "rpt-arrow", 22 | style: { 23 | backgroundColor: backgroundColor, 24 | boxShadow: boxShadow 25 | } 26 | }); 27 | }; 28 | 29 | var _default = Arrow; 30 | exports.default = _default; -------------------------------------------------------------------------------- /lib/Tooltip/TextBox.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireWildcard(require("react")); 9 | 10 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 11 | 12 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 13 | 14 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } 15 | 16 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 17 | 18 | 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); } } 19 | 20 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 21 | 22 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 23 | 24 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 25 | 26 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 27 | 28 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 29 | 30 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 31 | 32 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 33 | 34 | var TextBox = 35 | /*#__PURE__*/ 36 | function (_Component) { 37 | _inherits(TextBox, _Component); 38 | 39 | function TextBox() { 40 | var _getPrototypeOf2; 41 | 42 | var _this; 43 | 44 | _classCallCheck(this, TextBox); 45 | 46 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 47 | args[_key] = arguments[_key]; 48 | } 49 | 50 | _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(TextBox)).call.apply(_getPrototypeOf2, [this].concat(args))); 51 | 52 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", { 53 | hoverIndex: null, 54 | firstH: null, 55 | lastH: null, 56 | totH: null 57 | }); 58 | 59 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "unsetHover", function () { 60 | _this.setState({ 61 | hoverIndex: null 62 | }); 63 | 64 | _this.props.hoverArrow(false); 65 | }); 66 | 67 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "onSpanHover", function (index, lastIndex, numChildren) { 68 | _this.setState({ 69 | hoverIndex: index 70 | }); 71 | 72 | var _this$props = _this.props, 73 | rctStatic = _this$props.static, 74 | arrow = _this$props.arw, 75 | position = _this$props.pos, 76 | hoverArrow = _this$props.hoverArrow; 77 | 78 | if (!rctStatic && (index === 0 && (position.isSide('bottom') || arrow.isAlign('v-start')) || index === lastIndex && (position.isSide('top') || arrow.isAlign('v-end')) || numChildren === 1)) { 79 | return hoverArrow(true); 80 | } 81 | 82 | return hoverArrow(false); 83 | }); 84 | 85 | return _this; 86 | } 87 | 88 | _createClass(TextBox, [{ 89 | key: "componentDidMount", 90 | value: function componentDidMount() { 91 | var _this2 = this; 92 | 93 | var heights = Object.keys(this.spanHeights).map(function (key) { 94 | return _this2.spanHeights[key].clientHeight; 95 | }); 96 | var firstH = heights[0]; 97 | var lastH = heights[heights.length - 1]; 98 | var totH = heights.reduce(function (accumulator, currentValue) { 99 | return accumulator + currentValue; 100 | }, 0); 101 | this.setState({ 102 | totH: totH, 103 | firstH: firstH, 104 | lastH: lastH 105 | }); 106 | } 107 | }, { 108 | key: "render", 109 | value: function render() { 110 | var _this3 = this; 111 | 112 | var _this$props2 = this.props, 113 | arrow = _this$props2.arw, 114 | position = _this$props2.pos, 115 | lineSeparated = _this$props2.lines, 116 | tpStatic = _this$props2.static, 117 | width = _this$props2.textBoxWidth, 118 | shCol = _this$props2.shadowColor, 119 | shShape = _this$props2.shadowShape, 120 | move = _this$props2.move, 121 | backgroundColor = _this$props2.backgroundColor, 122 | padding = _this$props2.padding, 123 | borderRadius = _this$props2.borderRadius, 124 | hoverBackground = _this$props2.hoverBackground, 125 | hoverColor = _this$props2.hoverColor, 126 | alert = _this$props2.alert, 127 | flat = _this$props2.flat, 128 | children = _this$props2.children; 129 | var _this$state = this.state, 130 | hoverIndex = _this$state.hoverIndex, 131 | totH = _this$state.totH, 132 | firstH = _this$state.firstH, 133 | lastH = _this$state.lastH; 134 | 135 | var calcHPos = function calcHPos(left, center, right) { 136 | return position.isAlign('center') ? center : position.isAlign('left') ? left : right; 137 | }; 138 | 139 | var calcVPos = function calcVPos(perc, elHeight, divider, adjMove, totHeight) { 140 | return "calc(".concat(perc, "% - ").concat(totHeight || 0, "px - ").concat(elHeight, "px/").concat(divider, " + ").concat(adjMove || 0, "px)"); 141 | }; // TODO: REfactor 142 | 143 | 144 | var calcTopPos = function calcTopPos(elHeight, totHeight) { 145 | if (position.align === 'center') { 146 | return calcVPos(50, elHeight, 2, null, totHeight); 147 | } 148 | 149 | if (position.align === 'bottom') { 150 | return calcVPos(100, elHeight, 2, -12, totHeight); 151 | } 152 | 153 | return calcVPos(0, elHeight, 2, 12, totHeight); 154 | }; 155 | 156 | var numberChildren = _react.default.Children.count(children); 157 | 158 | var lastIndex = numberChildren - 1; 159 | this.spanHeights = {}; 160 | 161 | var adjChildren = _react.default.Children.map(children, function (child, index) { 162 | var style = { 163 | backgroundColor: backgroundColor, 164 | padding: padding 165 | }; 166 | if (width === 'auto') style.whiteSpace = 'nowrap'; 167 | 168 | if (!tpStatic && hoverIndex === index) { 169 | style.color = hoverColor; 170 | style.backgroundColor = hoverBackground; 171 | } 172 | 173 | if (lineSeparated && lastIndex !== index) { 174 | style.borderBottom = lineSeparated; 175 | } 176 | 177 | var ref = null; // eslint-disable-next-line 178 | 179 | ref = function ref(span) { 180 | return _this3.spanHeights["span".concat(index + 1)] = span; 181 | }; 182 | 183 | var childProps = _objectSpread({}, child.props, { 184 | ref: ref, 185 | style: style, 186 | onMouseOver: function onMouseOver() { 187 | return _this3.onSpanHover(index, lastIndex, numberChildren); 188 | } 189 | }); 190 | 191 | return _react.default.cloneElement(child, childProps); 192 | }); 193 | 194 | var left = ''; 195 | var right = ''; 196 | var top = '8px'; // Align: left, center, right 197 | 198 | var hLeftPos = calcHPos('100% - 50px', '50% - 40px', '0% - 30px'); 199 | var hRightPos = calcHPos('0% - 30px', '50% - 40px', '100% - 50px'); 200 | 201 | switch (arrow.position) { 202 | case 'h-start': 203 | left = "calc(".concat(hRightPos, ")"); 204 | break; 205 | 206 | case 'h-end': 207 | right = "calc(".concat(hLeftPos, ")"); 208 | break; 209 | 210 | case 'v-start': 211 | top = calcTopPos(firstH, null); 212 | break; 213 | 214 | case 'v-end': 215 | top = calcTopPos(lineSeparated ? -lastH + 1 : -lastH, totH); 216 | break; 217 | 218 | case 'v-center': 219 | top = "calc(0% - ".concat(totH, "px/2 + 11px)"); 220 | 221 | if (position.isAlign('center')) { 222 | top = "calc(50% - ".concat(totH, "px/2)"); 223 | } 224 | 225 | if (position.isAlign('bottom')) { 226 | top = "calc(100% - ".concat(totH, "px/2 - 11px)"); 227 | } 228 | 229 | break; 230 | 231 | default: 232 | break; 233 | } 234 | 235 | switch (position.side) { 236 | case 'top': 237 | top = calcVPos(0, totH, 1, 13); 238 | break; 239 | 240 | case 'left': 241 | right = '8px'; 242 | break; 243 | 244 | case 'right': 245 | left = '8px'; 246 | break; 247 | 248 | default: 249 | break; 250 | } 251 | 252 | var textBoxWidth = width; 253 | 254 | if (textBoxWidth !== 'auto') { 255 | textBoxWidth = Number(width.slice(0, -2)); 256 | if (move.left > 0) textBoxWidth += move.left; 257 | if (move.right > 0) textBoxWidth += move.right; 258 | } 259 | 260 | var boxStyle = { 261 | left: left, 262 | right: right, 263 | top: top, 264 | width: textBoxWidth, 265 | borderRadius: borderRadius 266 | }; 267 | var shColAdj = shCol.substr(0, shCol.lastIndexOf(',')).replace(/[)]/g, ','); 268 | var shadow = "".concat(shShape, " ").concat(shCol, ", 0 0 3px ").concat(shColAdj, ", 0.1), 0 0 0 1px ").concat(shColAdj, ", 0.15)"); 269 | var boxShadow = flat ? null : shadow; 270 | var alertStyle = alert ? 'rpt-alert' : ''; 271 | var rgb = alert || 'rgb(248, 109, 109)'; 272 | var alertShadow = alert ? "0 0 0 ".concat(rgb.slice(0, rgb.length - 1), ", 0.4)") : null; 273 | 274 | var noNeg = function noNeg(number) { 275 | return number > 0 ? number : 0; 276 | }; 277 | 278 | return _react.default.createElement("div", { 279 | className: "rpt-textbox-container ".concat(alertStyle), 280 | style: _objectSpread({}, boxStyle, { 281 | position: 'absolute', 282 | boxShadow: alertShadow, 283 | padding: "".concat(move.down, "px ").concat(move.left, "px ").concat(move.up, "px ").concat(move.right, "px") 284 | }) 285 | }, _react.default.createElement("div", { 286 | className: "rpt-shadow-container", 287 | style: { 288 | borderRadius: borderRadius, 289 | boxShadow: boxShadow, 290 | height: "calc(100% - ".concat(noNeg(move.down) + noNeg(move.up), "px)"), 291 | width: "calc(100% - ".concat(noNeg(move.left) + noNeg(move.right), "px)") 292 | } 293 | }), _react.default.createElement("div", { 294 | className: "rpt-textbox", 295 | onMouseLeave: this.unsetHover, 296 | style: { 297 | backgroundColor: backgroundColor, 298 | borderRadius: borderRadius 299 | } 300 | }, _react.default.createElement("div", { 301 | className: !tpStatic ? 'rpt-hover' : null, 302 | style: { 303 | borderRadius: borderRadius, 304 | overflow: 'hidden' 305 | } 306 | }, adjChildren))); 307 | } 308 | }]); 309 | 310 | return TextBox; 311 | }(_react.Component); 312 | 313 | var _default = TextBox; 314 | exports.default = _default; -------------------------------------------------------------------------------- /lib/Tooltip/styles.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | var cssRules = "\n.rpt-container {\n position: absolute;\n display: flex; \n}\n\n.rpt-bottom,\n.rpt-top {\n justify-content: center;\n width: 100%;\n}\n\n.rpt-right,\n.rpt-align-left {\n justify-content: flex-start;\n}\n\n.rpt-left,\n.rpt-align-right {\n justify-content: flex-end;\n}\n\n.rpt-align-center {\n align-items: center;\n}\n\n.rpt-align-bottom {\n align-items: flex-end;\n}\n\n.rpt-textbox {\n position: relative;\n z-index: 2;\n}\n\n.rpt-textbox span {\n display: block;\n cursor: text;\n box-sizing: border-box;\n}\n\n.rpt-textbox span p {\n font-size: 90%;\n font-weight: normal;\n line-height: 12px;\n color: inherit;\n opacity: 0.6;\n padding: 0;\n margin: 0;\n margin-top: 6px;\n}\n\n.rpt-hover span { \n cursor: pointer;\n}\n\n.rpt-shadow-container {\n width: 100%;\n height: 100%;\n z-index: 0;\n}\n\n.rpt-shadow-container,\n.rpt-textbox-container {\n position: absolute;\n animation: none; \n}\n\n.rpt-arrow {\ntransform: rotate(45deg);\nwidth: 15px;\nheight: 15px;\nmargin:3px;\nz-index: 1;\n}\n\n.rpt-alert {\n animation: rpt-alert 2s infinite;\n animation-fill-mode: initial;\n}\n\n@keyframes rpt-alert {\n 70% {box-shadow: 0 0 0 10px rgba(255, 255, 255, 0)}\n 100% {box-shadow: 0 0 0 10px rgba(255, 255, 255, 0)}\n}\n\n@keyframes rpt-bounce {\n 0% {transform: scale(0.8); opacity: 0.1}\n 60% {transform: scale(1.1); opacity: 1}\n 100% {transform: scale(1);}\n}\n\n@keyframes rpt-bounce-out {\n 20% { transform: scale(1.1); }\n 100% { transform: scale(0.8); opacity: 0.1 }\n}\n\n@keyframes rpt-fade {\n 0% { opacity: 0.1}\n 100% { opacity: 1}\n}\n\n@keyframes rpt-fade-out {\n 0% { opacity: 1 }\n 60% { opacity: 0 }\n 100% { opacity: 0}\n}\n"; 8 | var _default = cssRules; 9 | exports.default = _default; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireWildcard(require("react")); 9 | 10 | var _TextBox = _interopRequireDefault(require("./Tooltip/TextBox")); 11 | 12 | var _Arrow = _interopRequireDefault(require("./Tooltip/Arrow")); 13 | 14 | var _styles = _interopRequireDefault(require("./Tooltip/styles")); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 19 | 20 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } 21 | 22 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } 23 | 24 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 25 | 26 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 27 | 28 | 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); } } 29 | 30 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 31 | 32 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 33 | 34 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 35 | 36 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 37 | 38 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 39 | 40 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 41 | 42 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 43 | 44 | var Tooltip = 45 | /*#__PURE__*/ 46 | function (_Component) { 47 | _inherits(Tooltip, _Component); 48 | 49 | function Tooltip() { 50 | var _getPrototypeOf2; 51 | 52 | var _this; 53 | 54 | _classCallCheck(this, Tooltip); 55 | 56 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 57 | args[_key] = arguments[_key]; 58 | } 59 | 60 | _this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(Tooltip)).call.apply(_getPrototypeOf2, [this].concat(args))); 61 | 62 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "state", { 63 | hoverArrow: false, 64 | show: _this.props.show, 65 | mount: true, 66 | hasInitialized: false 67 | }); 68 | 69 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "hoverArrow", function (bool) { 70 | _this.setState({ 71 | hoverArrow: bool 72 | }); 73 | }); 74 | 75 | return _this; 76 | } 77 | 78 | _createClass(Tooltip, [{ 79 | key: "componentWillMount", 80 | value: function componentWillMount() { 81 | var _this2 = this; 82 | 83 | // Injecting styles directly into header 84 | if (!document.getElementById('rpt-css')) { 85 | var $style = document.createElement('style'); 86 | $style.type = 'text/css'; 87 | $style.id = 'rpt-css'; 88 | document.head.appendChild($style); 89 | $style.innerHTML = _styles.default; 90 | } // Basic prop type checking 91 | 92 | 93 | Object.keys(this.props).forEach(function (propName) { 94 | var type = _typeof(_this2.props[propName]); 95 | 96 | var text = "React-power-tooptip: [".concat(propName, "] prop should be a"); 97 | 98 | if (propName !== 'children' && type !== 'boolean' && type !== 'string') { 99 | // eslint-disable-next-line 100 | console.error("".concat(text, " string (check also units)")); 101 | } 102 | }); 103 | } 104 | }, { 105 | key: "shouldComponentUpdate", 106 | value: function shouldComponentUpdate(nextProps, nextState) { 107 | return nextProps !== this.props || nextState.hasInitialized !== this.state.hasInitialized || nextState.mount !== this.state.mount || nextState.hoverArrow !== this.state.hoverArrow; 108 | } 109 | }, { 110 | key: "componentDidUpdate", 111 | value: function componentDidUpdate() { 112 | /* eslint-disable */ 113 | if (!this.state.hasInitialized) this.setState({ 114 | show: this.props.show, 115 | hasInitialized: true 116 | }); 117 | if (this.props.show) this.setState({ 118 | mount: true 119 | }); 120 | if (!this.props.animation) this.setState({ 121 | mount: false 122 | }); 123 | /* eslint-disable */ 124 | } 125 | }, { 126 | key: "render", 127 | value: function render() { 128 | var _this3 = this; 129 | 130 | var _this$props = this.props, 131 | lines = _this$props.lineSeparated, 132 | pos = _this$props.position, 133 | hoverBackground = _this$props.hoverBackground, 134 | backgroundColor = _this$props.backgroundColor, 135 | arwAlign = _this$props.arrowAlign, 136 | moveDown = _this$props.moveDown, 137 | moveRight = _this$props.moveRight, 138 | moveLeft = _this$props.moveLeft, 139 | moveUp = _this$props.moveUp, 140 | textAlign = _this$props.textAlign, 141 | fontFamily = _this$props.fontFamily, 142 | fontWeight = _this$props.fontWeight, 143 | fontSize = _this$props.fontSize, 144 | color = _this$props.color, 145 | animation = _this$props.animation, 146 | zIndex = _this$props.zIndex, 147 | show = _this$props.show, 148 | flat = _this$props.flat; // Sets if false no line; if true default line; if string custom line; 149 | 150 | var lineSeparated = typeof lines === 'boolean' ? '1px solid #ececec' : lines; 151 | 152 | function isAlign(str) { 153 | return this.align ? this.align === str : this.position === str; 154 | } 155 | 156 | function isSide(str) { 157 | return this.side === str; 158 | } 159 | 160 | var position = { 161 | side: pos.split(' ')[0], 162 | align: pos.split(' ')[1], 163 | isAlign: isAlign, 164 | isSide: isSide 165 | }; 166 | var arrow = { 167 | isAlign: isAlign, 168 | position: arwAlign 169 | }; 170 | var side = position.side, 171 | align = position.align; 172 | var classes = ['rpt-container']; 173 | var tooltipStyle = {}; 174 | var bottom; 175 | 176 | var arrange = function arrange(top, left, right, height, width, cssSel) { 177 | tooltipStyle = { 178 | top: top, 179 | left: left, 180 | right: right, 181 | height: height, 182 | width: width 183 | }; 184 | classes.push(cssSel); 185 | }; 186 | 187 | switch (side) { 188 | case 'bottom': 189 | arrange('100%', '0px', '', '', '100%', 'rpt-bottom'); 190 | break; 191 | 192 | case 'top': 193 | arrange('', '0px', '', '', '100%', 'rpt-top'); 194 | bottom = '100%'; 195 | break; 196 | 197 | case 'right': 198 | arrange('0px', '100%', '', '100%', '', 'rpt-right'); 199 | break; 200 | 201 | default: 202 | arrange('0px', '', '100%', '100%', '', 'rpt-left'); 203 | break; 204 | } 205 | 206 | var onAxis = { 207 | y: position.isSide('top') || position.isSide('bottom'), 208 | x: position.isSide('left') || position.isSide('right') 209 | }; 210 | arrow.position = onAxis.y ? "h-".concat(arrow.position) : "v-".concat(arrow.position); 211 | 212 | var num = function num(str) { 213 | return Number(str.slice(0, -2)); 214 | }; 215 | 216 | var move = { 217 | down: num(moveDown), 218 | up: num(moveUp), 219 | left: num(moveLeft), 220 | right: num(moveRight) 221 | }; 222 | var oneMovePropIsNeg = move.down < 0 || move.up < 0 || move.left < 0 || move.right < 0; 223 | 224 | switch (align) { 225 | case 'left': 226 | if (onAxis.y) classes.push('rpt-align-left'); 227 | break; 228 | 229 | case 'right': 230 | if (onAxis.y) classes.push('rpt-align-right'); 231 | break; 232 | 233 | case 'bottom': 234 | if (onAxis.x) classes.push('rpt-align-bottom'); 235 | break; 236 | 237 | case 'top': 238 | break; 239 | 240 | default: 241 | if (onAxis.x) { 242 | classes.push('rpt-align-center'); 243 | 244 | if (!oneMovePropIsNeg) { 245 | move.down *= 2; 246 | move.up *= 2; 247 | } 248 | } 249 | 250 | if (onAxis.y && !oneMovePropIsNeg) { 251 | move.right *= 2; 252 | move.left *= 2; 253 | } 254 | 255 | break; 256 | } 257 | 258 | var adjustment = "".concat(move.down, "px ").concat(move.left, "px ").concat(move.up, "px ").concat(move.right, "px"); 259 | tooltipStyle = _objectSpread({}, tooltipStyle, { 260 | zIndex: zIndex, 261 | color: color, 262 | bottom: bottom, 263 | fontSize: fontSize, 264 | textAlign: textAlign, 265 | fontFamily: fontFamily, 266 | fontWeight: fontWeight, 267 | padding: oneMovePropIsNeg ? null : adjustment, 268 | margin: oneMovePropIsNeg ? adjustment : null, 269 | animation: show ? "rpt-".concat(animation, " 0.2s") : "rpt-".concat(animation, "-out 0.15s") 270 | }); 271 | return !animation && show || this.state.show && this.state.mount ? _react.default.createElement("div", { 272 | className: classes.join(' '), 273 | style: tooltipStyle, 274 | onAnimationEnd: function onAnimationEnd() { 275 | if (!show && animation) _this3.setState({ 276 | mount: false 277 | }); 278 | } 279 | }, _react.default.createElement("div", { 280 | style: { 281 | display: 'flex', 282 | justifyContent: 'center' 283 | } 284 | }, _react.default.createElement(_Arrow.default, { 285 | isHovered: this.state.hoverArrow, 286 | hovBkg: hoverBackground, 287 | bkgCol: backgroundColor, 288 | flat: flat 289 | }), _react.default.createElement(_TextBox.default, _extends({}, this.props, { 290 | hoverArrow: this.hoverArrow, 291 | lines: lineSeparated, 292 | pos: position, 293 | arw: arrow, 294 | move: move 295 | })))) : null; 296 | } 297 | }]); 298 | 299 | return Tooltip; 300 | }(_react.Component); // Specifies the default values for props: 301 | 302 | 303 | Tooltip.defaultProps = { 304 | hoverBackground: '#ececec', 305 | hoverColor: 'black', 306 | backgroundColor: 'white', 307 | textBoxWidth: '150px', 308 | padding: '15px 20px', 309 | borderRadius: '5px', 310 | shadowColor: 'rgba(0,0,0,0.251)', 311 | shadowShape: '0 8px 15px', 312 | moveDown: '0px', 313 | moveRight: '0px', 314 | moveLeft: '0px', 315 | moveUp: '0px', 316 | position: 'right center', 317 | arrowAlign: 'start', 318 | textAlign: 'left', 319 | fontFamily: 'inherit', 320 | fontWeight: 'bold', 321 | fontSize: 'inherit', 322 | color: 'inherit', 323 | zIndex: '100', 324 | animation: '' 325 | }; 326 | var _default = Tooltip; 327 | exports.default = _default; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-power-tooltip", 3 | "version": "1.0.2", 4 | "description": "A powerful tooltip and menu component library for react.", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "dev": "concurrently \"npm run lib:watch\" \"npm run docs\"", 8 | "lib": "babel src/lib -d lib --copy-files", 9 | "lib:watch": "babel src/lib -w -d lib --copy-files", 10 | "docs": "webpack-dev-server --mode development --config webpack/dev.config.js", 11 | "docs:prod": "webpack --mode production --config webpack/prod.config.js", 12 | "test": "NODE_ENV=test jest --coverage --coverageReporters=text-lcov | coveralls", 13 | "test:watch": "jest --watch", 14 | "mock:create": "webpack --mode development --config webpack/test.config.js", 15 | "lint": "eslint --ext .js,.jsx src/lib", 16 | "prepublish": "npm run lib", 17 | "postinstall": "node postinstall.js" 18 | }, 19 | "keywords": [ 20 | "tooltip", 21 | "notification", 22 | "menu", 23 | "react", 24 | "react-component" 25 | ], 26 | "license": "MIT", 27 | "peerDependencies": { 28 | "react": "^15.3.0 || ^16.2.0", 29 | "react-dom": "^15.3.0 || ^16.2.0" 30 | }, 31 | "devDependencies": { 32 | "@babel/cli": "^7.1.2", 33 | "@babel/core": "^7.1.2", 34 | "@babel/plugin-proposal-class-properties": "^7.1.0", 35 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 36 | "@babel/preset-env": "^7.1.0", 37 | "@babel/preset-react": "^7.0.0", 38 | "babel-core": "^7.0.0-bridge.0", 39 | "babel-eslint": "^10.0.1", 40 | "babel-loader": "^8.0.0", 41 | "babel-plugin-prismjs": "^1.0.2", 42 | "concurrently": "^3.5.1", 43 | "coveralls": "3.0.2", 44 | "css-loader": "^0.28.11", 45 | "enzyme": "^3.7.0", 46 | "enzyme-adapter-react-16": "^1.6.0", 47 | "enzyme-to-json": "^3.3.4", 48 | "eslint": "^5.8.0", 49 | "eslint-config-airbnb": "^17.1.0", 50 | "eslint-plugin-import": "^2.14.0", 51 | "eslint-plugin-jsx-a11y": "^6.1.2", 52 | "eslint-plugin-react": "^7.11.1", 53 | "favicons-webpack-plugin": "0.0.9", 54 | "file-loader": "^2.0.0", 55 | "html-webpack-plugin": "^3.2.0", 56 | "jest": "^23.6.0", 57 | "jest-css-modules": "^1.1.0", 58 | "jest-enzyme": "^7.0.0", 59 | "prismjs": "^1.15.0", 60 | "puppeteer": "^1.10.0", 61 | "react": "^16.3.2", 62 | "react-dom": "^16.3.2", 63 | "react-prism": "^4.3.2", 64 | "style-loader": "^0.21.0", 65 | "webpack": "^4.6.0", 66 | "webpack-cli": "^3.1.2", 67 | "webpack-dev-server": "^3.1.3" 68 | }, 69 | "author": "Justin Rhodes ", 70 | "homepage": "https://github.com/justinrhodes1/react-power-tooltip", 71 | "repository": { 72 | "type": "git", 73 | "url": "git@github.com:justinrhodes1/react-power-tooltip.git" 74 | }, 75 | "dependencies": {}, 76 | "jest": { 77 | "snapshotSerializers": [ 78 | "enzyme-to-json/serializer" 79 | ], 80 | "moduleNameMapper": { 81 | "\\.(css)$": "jest-css-modules" 82 | }, 83 | "setupTestFrameworkScriptFile": "/jest.setup.js" 84 | } 85 | } -------------------------------------------------------------------------------- /postinstall.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | console.log('\x1b[36m%s\x1b[0m', 'Thanks for choosing react-power-tooltip!\n' 3 | + 'Do get involved: https://github.com/justinrhodes1/react-power-tooltip'); 4 | -------------------------------------------------------------------------------- /puppeteer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Test 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /puppeteer/input/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Test 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /puppeteer/input/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import Tooltip from '../../src/lib'; 4 | 5 | const Test = () => { 6 | const targetStyle = { 7 | position: 'relative', 8 | margin: '200px', 9 | width: '100px', 10 | height: '100px', 11 | background: 'grey', 12 | boxSizing: 'border-box' 13 | }; 14 | 15 | return ( 16 | <> 17 |
21 | 25 | Option 1 26 | Option 2 27 | Option 3 28 | 29 |
30 |
34 | 38 | Option 1 39 | Option 2 40 | Option 3 41 | 42 |
43 |
47 | 51 | Option 1 52 | Option 2 53 | Option 3 54 | 55 |
56 | 57 | ); 58 | }; 59 | 60 | export default Test; 61 | 62 | render(, document.getElementById('test')); 63 | -------------------------------------------------------------------------------- /src/assets/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinrhodes1/react-power-tooltip/fb2df8120e0d64ed81c53fcaaef845157f1a9e0c/src/assets/favicon.png -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 48 | 52 | 53 | 54 | 72 | 77 | 78 | 80 | 81 | 83 | image/svg+xml 84 | 86 | 87 | 88 | 89 | 90 | 95 | 100 | 103 | 109 | 110 | 113 | 118 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/docs/Content/AdvancedUse.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const AdvUsage = () => { 4 | return ( 5 | <> 6 |

Advanced Usage

7 |

Custom Animations

8 |

9 | To customize mount and umount animations 10 |

11 |
    12 |
  1. Add your own CSS @keyframes rules to the global scope
  2. 13 |
  3. Enable react-power-tooptip to use your animations
  4. 14 |
15 |

16 | Add your keyframe rules to the 17 | global scope 18 | using the same selector for both mount and unmount related keyframes. All 19 | keyframe selectors require an 20 | 'rpt-' 21 | prefix. Unmount related keyframes also require an 22 | '-out' 23 | suffix. 24 |

25 |
26 |         
27 |           {`/* Global Scope */
28 | @keyframes rpt-slideUpDown {
29 |   0%   { transform: translateY(20px); opacity: 0 }
30 |   100% { transform: translateY(0px); opacity: 1 }
31 | }
32 | 
33 | @keyframes rpt-slideUpDown-out {
34 |   0%   { transform: translateY(0px); opacity: 1 }
35 |   100% { transform: translateY(20px); opacity: 0 }
36 | }`}
37 |         
38 |       
39 |

40 | To enable your animations, add your custom keyframe name ( 41 | without 42 | pre- or suffixes) to your tooltip via the animation prop. 43 |

44 |
45 |         
46 |           {`
50 |     Some text
51 | `}
52 |         
53 |       
54 | 55 | ); 56 | }; 57 | 58 | export default AdvUsage; 59 | -------------------------------------------------------------------------------- /src/docs/Content/Api.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Api = () => { 4 | return ( 5 | <> 6 |

API Overview

7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 144 | 145 | 146 | 147 | 152 | 153 | 158 | 159 | 160 |
PropsType / OptionsDefaultDescription
showboolean: false | truefalseMount tooltip if true
fontFamilystring: fontFamily'inherit'Font family of text
fontSizestring: px'inherit'Font size of text
fontWeightstring'bold'Font weight of text
colorstring'inherit'Font color of text
animationstring: fade | bounce'fade' 51 | Mount/Unmount anmation. For custom animations see examples for more 52 | info 53 | . 54 |
hoverBackgroundstring: hex colors'#ececec'Background color on hover
hoverColorstring: hex colors'#000000'Font color on hover
backgroundColorstring: hex colors'#ffffff'Background color
alertstring: rgb colorsfalsePulse animation
textBoxWidthstring: px | auto'150px'Width of the text box
paddingstring: px'15px 20px'Padding of text
borderRadiusstring: px'5px'Radius of corners
zIndexstring: number'100'Z-index of tooltip.
moveDownstring: px'0px'Downward position adjustment
moveRightstring: px'0px'Right position adjustment
staticboolean: false | truefalseDisable hover animations
flatboolean: false | truefalseDisable shadows
lineSeparatedstring: css border property'1px solid #ececec'Specifies line separation between options (optional).
arrowAlignstring: 'start' | 'center' | 'end''start' 139 | Vertical or horizontal aligning along the left/right or 140 | top/bottom textbox side respectively. See 141 | examples 142 | for more information. 143 |
position 148 |

149 | string: 'position1 position2' 150 |

151 |
'right center' 154 | Positions tooltip relative to target. See 155 | examples 156 | for more information. 157 |
161 |
162 | 163 | ); 164 | }; 165 | 166 | export default Api; 167 | -------------------------------------------------------------------------------- /src/docs/Content/BasicUse.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { codeBasicComp, codeBasicTp } from './CodeSnippets'; 3 | 4 | const BasicUsage = () => { 5 | return ( 6 | <> 7 |

Basic Usage

8 |

9 | 1) Import the tooltip into your stateful react component file (see below). 10 |

11 |
12 |         
13 |           import Tooltip from 'react-power-tooltip'
14 |         
15 |       
16 |

17 | 2) Add a hover state & mouse event handler to the enclosing target component. 18 |

19 |

20 | Important: 21 | Set the position of the target component to 22 | relative 23 | . 24 |

25 |
26 |         
27 |           {codeBasicComp}
28 |         
29 |       
30 |

31 | 3) Add the tooltip inside the target element. 32 |

33 |
34 |         
35 |           {codeBasicTp}
36 |         
37 |       
38 | 39 | ); 40 | }; 41 | 42 | export default BasicUsage; 43 | -------------------------------------------------------------------------------- /src/docs/Content/CodeSnippets.jsx: -------------------------------------------------------------------------------- 1 | const codeInstallNpm = '$ npm install react-power-tooltip --save'; 2 | const codeBasicComp = `class Example extends Component { 3 | state = { 4 | show: false 5 | } 6 | 7 | showTooltip = bool => { 8 | this.setState({ show: bool }) 9 | } 10 | 11 | render() { 12 | return ( 13 | {/* Target element position needs to be RELATIVE */} 14 |
this.showTooltip(true)} 17 | onMouseLeave={() => this.showTooltip(false)}> 18 | 19 | {/* ADD TOOLTIP HERE */} 20 | 21 |
22 | ); 23 | } 24 | } 25 | export default Example;`; 26 | 27 | const codeBasicTp = `{/* Add options/text via span elements */} 28 | 29 | Some text 30 | `; 31 | 32 | const codeHoverable = `{/* Hoverable */} 33 | 35 | Some text 36 | `; 37 | 38 | const codeStatic = `{/* Static */} 39 | 42 | Some text 43 | `; 44 | 45 | const codeAlert = `{/* Alert */} 46 | 49 | Some text 50 | `; 51 | 52 | 53 | const codeFade = `{/* Fade */} 54 | 57 | Some text 58 | `; 59 | 60 | const codeSlideUpDown = `{/* SlideUpDown */} 61 | 64 | Some text 65 | `; 66 | 67 | const codeBounce = `{/* Bounce */} 68 | 71 | Some text 72 | `; 73 | 74 | const codeAlign = `{/* Positions tooltip central on the left side of the target element */} 75 | 79 | Some text 80 | `; 81 | 82 | const codeDefault = `{/* Default */} 83 | 86 | Some text 87 |

Some subtext

88 |
89 |
`; 90 | 91 | const codeFont = `{/* Text & Font */} 92 | 98 | Some text 99 | `; 100 | 101 | const codeCorners = `{/* Corners & Lines */} 102 | 107 | Some text 108 | `; 109 | 110 | const codeHover = `{/* Hover color */} 111 | 117 | Some text 118 | `; 119 | 120 | const codeBackground = `{/* Hover color */} 121 | 126 | Some text 127 | `; 128 | 129 | const codeFlat = `{/* Hover color */} 130 | 137 | Some text 138 | `; 139 | 140 | const codeTop = `{/* Arrow top */} 141 | 146 | Some text 147 | `; 148 | 149 | const codeCenterV = `{/* Arrow center */} 150 | 155 | Some text 156 | `; 157 | 158 | const codeBottom = `{/* Arrow bottom */} 159 | 164 | Some text 165 | `; 166 | 167 | const codeLeft = `{/* Arrow left */} 168 | 174 | Some text 175 | `; 176 | 177 | const codeRight = `{/* Arrow right */} 178 | 184 | Some text 185 | `; 186 | 187 | const codeCenterH = `{/* Arrow center */} 188 | 194 | Some text 195 | `; 196 | 197 | const codeMoveUpNeg = `{/* Move down into target */} 198 | 204 | Some text 205 | `; 206 | 207 | const codeMoveLeftNeg = `{/* Move right into target */} 208 | 214 | Some text 215 | `; 216 | 217 | const codeMoveUp = `{/* Move up out of target */} 218 | 224 | Some text 225 | `; 226 | 227 | const codeMoveLeft = `{/* Move left out of target */} 228 | 234 | Some text 235 | `; 236 | 237 | export { 238 | codeStatic, 239 | codeAlert, 240 | codeHoverable, 241 | codeFade, 242 | codeSlideUpDown, 243 | codeBounce, 244 | codeAlign, 245 | codeDefault, 246 | codeFont, 247 | codeCorners, 248 | codeHover, 249 | codeBackground, 250 | codeFlat, 251 | codeTop, 252 | codeCenterV, 253 | codeBottom, 254 | codeLeft, 255 | codeCenterH, 256 | codeRight, 257 | codeMoveUpNeg, 258 | codeMoveLeftNeg, 259 | codeMoveUp, 260 | codeMoveLeft, 261 | codeInstallNpm, 262 | codeBasicComp, 263 | codeBasicTp 264 | }; 265 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Animation/Bounce.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | class Animations2 extends Component { 5 | state = { 6 | show: false 7 | } 8 | 9 | showTooltip = (bool) => { 10 | this.setState({ show: bool }); 11 | } 12 | 13 | render() { 14 | return ( 15 |
this.showTooltip(true)} 17 | onMouseLeave={() => this.showTooltip(false)} 18 | className="hoverDiv" 19 | > 20 | Hover Me 21 | Bounce 22 | 30 | Our Technology 31 | Our Story 32 | 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default Animations2; 39 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Animation/FadeIn.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | class Animations3 extends Component { 5 | state = { 6 | show: false 7 | } 8 | 9 | showTooltip = (bool) => { 10 | this.setState({ show: bool }); 11 | } 12 | 13 | render() { 14 | return ( 15 |
this.showTooltip(true)} 17 | onMouseLeave={() => this.showTooltip(false)} 18 | className="hoverDiv" 19 | > 20 | Hover Me 21 | Fade 22 | 30 | Our Technology 31 | Our Story 32 | 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default Animations3; 39 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Animation/SlideIn.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | class Animations1 extends Component { 5 | state = { 6 | show: false 7 | } 8 | 9 | showTooltip = (bool) => { 10 | this.setState({ show: bool }); 11 | } 12 | 13 | render() { 14 | return ( 15 |
this.showTooltip(true)} 17 | onMouseLeave={() => this.showTooltip(false)} 18 | className="hoverDiv" 19 | > 20 | Hover Me 21 | SlideUpDown 22 | 30 | Our Technology 31 | Our Story 32 | 33 |
34 | ); 35 | } 36 | } 37 | 38 | export default Animations1; 39 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Animation/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SlideIn from './SlideIn'; 4 | import Bounce from './Bounce'; 5 | import FadeIn from './FadeIn'; 6 | 7 | const Animation = () => { 8 | return ( 9 | <> 10 |

11 | Hover effects 12 |

13 |
14 | 15 | 16 | 17 |
18 | 19 | ); 20 | }; 21 | 22 | export default Animation; 23 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Customization/Colors.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const Colors = () => { 5 | return ( 6 | <> 7 |

Colors

8 |
9 |
10 |

Hover / lines

11 |
12 | 18 | Our Technology 19 | Our Story 20 | 21 |
22 |
23 |
24 |

Background / shadows

25 |
26 | 32 | Our Technology 33 | Our Story 34 | 35 |
36 |
37 |
38 |

Flat (no shadows)

39 |
40 | 48 | Our Technology 49 | Our Story 50 | 51 |
52 |
53 |
54 | 55 | ); 56 | }; 57 | 58 | export default Colors; 59 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Customization/Shapes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const Shapes = () => { 5 | return ( 6 | <> 7 |

Shapes / Fonts

8 |
9 |
10 |

Default

11 |
12 | 15 | 16 | Our Technology 17 |

Some text

18 |
19 | Our Story 20 |
21 |
22 |
23 |
24 |

Text / shadows

25 |
26 | 33 | Our Technology 34 | Our Story 35 | 36 |
37 |
38 |
39 |

Corners / lines

40 |
41 | 46 | Our Technology 47 | Our Story 48 | 49 |
50 |
51 |
52 | 53 | ); 54 | }; 55 | 56 | export default Shapes; 57 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Positions/AdjPosIn.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const Adjustment = () => { 5 | return ( 6 | <> 7 |

Inner positions

8 |
9 |
22 |

Target element

23 | 33 | 34 | moveLeft 35 |
36 | -150px 37 |
38 |
39 | 49 | 50 | moveDown 51 |
52 | -100px 53 |
54 |
55 | 65 | 66 | moveRight 67 |
68 | -150px 69 |
70 |
71 | 81 | 82 | moveUp 83 |
84 | -100px 85 |
86 |
87 |
88 |
89 | 90 | ); 91 | }; 92 | 93 | export default Adjustment; 94 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Positions/AdjPosOut.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const AdjustmentOuter = () => { 5 | return ( 6 | <> 7 |

Outer positions

8 |
9 |
25 |

Target element

26 | 36 | 37 | moveLeft 38 |
39 | 40px 40 |
41 |
42 | 52 | 53 | moveDown 54 |
55 | 40px 56 |
57 |
58 | 68 | 69 | moveRight 70 |
71 | 40px 72 |
73 |
74 | 84 | 85 | moveUp 86 |
87 | 40px 88 |
89 |
90 |
91 |
92 | 93 | ); 94 | }; 95 | 96 | export default AdjustmentOuter; 97 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Positions/Align.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | class Align extends Component { 5 | state = { 6 | hover: false 7 | } 8 | 9 | hover = (side) => { 10 | this.setState({ hover: side }); 11 | } 12 | 13 | render() { 14 | const { hover } = this.state; 15 | return ( 16 |
17 | 24 | Top 25 | 26 | 33 | Center 34 | 35 | 42 | Bottom 43 | 44 | 51 | Top 52 | 53 | 60 | Center 61 | 62 | 69 | Bottom 70 | 71 | 78 | Left 79 | 80 | 87 | Center 88 | 89 | 96 | Right 97 | 98 | 105 | Left 106 | 107 | 114 | Center 115 | 116 | 123 | Right 124 | 125 |
126 |
136 |
143 | Left 144 | Right 145 |
146 |
147 |
157 |
165 | Top 166 | Bottom 167 |
168 |
169 |
this.hover('left')} 172 | onMouseLeave={() => this.hover(false)} 173 | /> 174 |
this.hover('top')} 177 | onMouseLeave={() => this.hover(false)} 178 | /> 179 |
this.hover('right')} 182 | onMouseLeave={() => this.hover(false)} 183 | /> 184 |
this.hover('bottom')} 187 | onMouseLeave={() => this.hover(false)} 188 | /> 189 |
190 |
191 | ); 192 | } 193 | } 194 | 195 | export default Align; 196 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Positions/ArrowHPos.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const Arrow = () => { 5 | return ( 6 | <> 7 |

8 | Horizontally 9 | (when tooltip positioned above / below target) 10 |

11 |
12 |
13 |

14 | Arrow align: 15 | start 16 |

17 |
18 | 25 | Longer Option 1 26 | Longer Option 2 27 | 28 |
29 |
30 |
31 |

32 | Arrow align: 33 | center 34 |

35 |
36 | 43 | Longer Option 1 44 | Longer Option 2 45 | 46 | 47 |
48 |
49 |
50 |

51 | Arrow align: 52 | end 53 |

54 |
55 | 62 | Longer Option 1 63 | Longer Option 2 64 | 65 |
66 |
67 |
68 | 69 | ); 70 | }; 71 | 72 | export default Arrow; 73 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Positions/ArrowVPos.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const VerticalArrow = () => { 5 | return ( 6 | <> 7 |

8 | Vertically 9 | (when tooltip positioned left / right of target) 10 |

11 |
12 |
13 |

14 | Arrow align: 15 | start 16 |

17 |
18 | 23 | Longer Option 1 24 | Longer Option 2 25 | 26 |
27 |
28 |
29 |

30 | Arrow align: 31 | center 32 |

33 |
34 | 40 | Longer Option 1 41 | Longer Option 2 42 | 43 |
44 |
45 |
46 |

47 | Arrow align: 48 | end 49 |

50 |
51 | 57 | Longer Option 1 58 | Longer Option 2 59 | 60 |
61 |
62 |
63 | 64 | ); 65 | }; 66 | 67 | export default VerticalArrow; 68 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Types/Alert.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | import logo from '../../../../assets/logo.svg'; 4 | 5 | class Alert extends Component { 6 | state = { 7 | alternate: false 8 | } 9 | 10 | componentDidMount() { 11 | setInterval(() => { 12 | this.setState(prevState => ({ alternate: !prevState.alternate })); 13 | }, 5000); 14 | } 15 | 16 | render() { 17 | return ( 18 | <> 19 |

Static alert example

20 |
35 | 43 |
51 | 54 | Shopping Cart 55 | 66 | New Item added! 67 | 68 | 69 | Contact 70 |
71 |
72 | 73 | ); 74 | } 75 | } 76 | 77 | export default Alert; 78 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/Types/Types.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../../../lib'; 3 | 4 | const Types = () => { 5 | return ( 6 |
7 |
8 |

9 | Hoverable 10 | (default) 11 |

12 |
13 | 18 | Our Technology 19 | Our story 20 | 21 |
22 |
23 |
24 |

25 | Static 26 | (via static prop) 27 |

28 |
29 | 35 | Our story: 36 | Show static text which provides some additional information. 37 | 38 |
39 |
40 |
41 |

42 | Alert 43 | (via alert prop) 44 |

45 |
46 | 52 | Our Technology 53 | Our story 54 | 55 |
56 |
57 |
58 | ); 59 | }; 60 | 61 | export default Types; 62 | -------------------------------------------------------------------------------- /src/docs/Content/Examples/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Animation from './Animation'; 3 | import Types from './Types/Types'; 4 | import Alert from './Types/Alert'; 5 | import Colors from './Customization/Colors'; 6 | import Shapes from './Customization/Shapes'; 7 | import AlignPositions from './Positions/Align'; 8 | import ArrowVPos from './Positions/ArrowVPos'; 9 | import ArrowHPos from './Positions/ArrowHPos'; 10 | import AdjustmentInner from './Positions/AdjPosIn'; 11 | import AdjustmentOuter from './Positions/AdjPosOut'; 12 | import ToggleCode from '../ToggleCode'; 13 | import { 14 | codeStatic, 15 | codeAlert, 16 | codeHoverable, 17 | codeFade, 18 | codeSlideUpDown, 19 | codeBounce, 20 | codeAlign, 21 | codeDefault, 22 | codeFont, 23 | codeCorners, 24 | codeHover, 25 | codeBackground, 26 | codeFlat, 27 | codeTop, 28 | codeCenterV, 29 | codeBottom, 30 | codeLeft, 31 | codeCenterH, 32 | codeRight, 33 | codeMoveUpNeg, 34 | codeMoveLeftNeg, 35 | codeMoveUp, 36 | codeMoveLeft 37 | } from '../CodeSnippets'; 38 | 39 | class Examples extends Component { 40 | render() { 41 | return ( 42 | <> 43 |

Examples

44 |

Types of tooltips

45 |
51 | 52 |
53 | 54 |
55 |
56 |
 57 |                 
 58 |                   {codeHoverable}
 59 |                 
 60 |               
61 |
 62 |                 
 63 |                   {codeStatic}
 64 |                 
 65 |               
66 |
 67 |                 
 68 |                   {codeAlert}
 69 |                 
 70 |               
71 |
72 |
73 |
74 | 75 |

Animations

76 |

77 | Note: 78 | The slideUpDown animation is 79 | not 80 | included in this library. Check out the 81 | advanced section 82 | for more info on how to add your own custom animations to this library. 83 |

84 |
90 | 91 |
92 | 93 |
94 |
95 |
 96 |                 
 97 |                   {codeFade}
 98 |                 
 99 |               
100 |
101 |                 
102 |                   {codeSlideUpDown}
103 |                 
104 |               
105 |
106 |                 
107 |                   {codeBounce}
108 |                 
109 |               
110 |
111 |
112 |
113 |

Customization

114 |
120 | 121 |
122 | 123 |
124 |
125 |
126 |                 
127 |                   {codeDefault}
128 |                 
129 |               
130 |
131 |                 
132 |                   {codeFont}
133 |                 
134 |               
135 |
136 |                 
137 |                   {codeCorners}
138 |                 
139 |               
140 |
141 |
142 |
143 |
150 | 151 |
152 | 153 |
154 |
155 |
156 |                 
157 |                   {codeHover}
158 |                 
159 |               
160 |
161 |                 
162 |                   {codeBackground}
163 |                 
164 |               
165 |
166 |                 
167 |                   {codeFlat}
168 |                 
169 |               
170 |
171 |
172 |
173 |

Positions

174 |

175 | 1) 176 | Arrow align 177 | relative to textbox (via arrowAlign prop) 178 |

179 | The arrowAlign prop aligns the arrow 180 | relative to the textbox side 181 | : 182 |

183 |
    184 |
  1. 185 | arrowAlign: 186 | start 187 | = Aligns the arrow on the textbox side either on the top or left depending on the 188 | tooltip position. 189 |
  2. 190 |
  3. 191 | arrowAlign: 192 | center 193 | = Aligns the arrow central on the textbox side. 194 |
  4. 195 |
  5. 196 | arrowAlign: 197 | end 198 | = Aligns the arrow on the textbox side either on the bottom or right depending on the 199 | tooltip position. 200 |
  6. 201 |
202 | Note: 203 | This prop does NOT determine the textbox side on which the 204 | arrow is placed (done implicitly via the position prop). 205 |

206 |
212 | 213 |
214 | 215 |
216 |
217 |
218 |                 
219 |                   {codeTop}
220 |                 
221 |               
222 |
223 |                 
224 |                   {codeCenterV}
225 |                 
226 |               
227 |
228 |                 
229 |                   {codeBottom}
230 |                 
231 |               
232 |
233 |
234 |
235 |
238 | 239 |
240 | 241 |
242 |
243 |
244 |                 
245 |                   {codeLeft}
246 |                 
247 |               
248 |
249 |                 
250 |                   {codeCenterH}
251 |                 
252 |               
253 |
254 |                 
255 |                   {codeRight}
256 |                 
257 |               
258 |
259 |
260 |
261 |

262 | 2) 263 | Tooltip positions 264 | relative to target element (via position prop) 265 |

266 |

267 | The position prop positions the tooltip 268 | relative to target element. 269 | It consists of a string with two positions: 270 |
    271 |
  1. 272 | First position: 273 | determins 274 | on which side 275 | of the target element to position the tooltip. 276 |
  2. 277 |
  3. 278 | Second position: 279 | determins 280 | how to align 281 | the tooltip on that side. 282 |
  4. 283 |
284 | Note: 285 | The position prop implicitly determines on which textbox side the arrow will 286 | appear. To position the arrow relative to that textbox side use the arrowAlign prop. 287 |

288 |
289 |           
290 |             {codeAlign}
291 |           
292 |         
293 |
301 |
302 | 303 |
304 |
305 |

306 | 3) 307 | Adjusting 308 | positions (via move props) 309 |

310 |
318 | 319 |
320 | 321 |
322 |
323 |
324 |                 
325 |                   {codeMoveUpNeg}
326 |                 
327 |               
328 |
329 |                 
330 |                   {codeMoveLeftNeg}
331 |                 
332 |               
333 |
334 |             
335 |
336 |
337 |

338 | Positive move prop values extend the hoverable area from the target 339 | to the tooltip element. This allows the selection of tooltip options 340 | when the target is hovered. 341 |

342 |
350 | 351 |
352 | 353 |
354 |
355 |
356 |                 
357 |                   {codeMoveUp}
358 |                 
359 |               
360 |
361 |                 
362 |                   {codeMoveLeft}
363 |                 
364 |               
365 |
366 |             
367 |
368 |
369 | 370 | ); 371 | } 372 | } 373 | 374 | export default Examples; 375 | -------------------------------------------------------------------------------- /src/docs/Content/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../lib'; 3 | import '../styles.css'; 4 | 5 | const Header = () => { 6 | return ( 7 |
8 | 16 |

17 | React-power- 18 | 21 | tooltip 22 | 32 | Simple & Easy! 33 | 34 | 35 |

36 |

A flexible, lightweight tooltip & menu library.

37 |
46 | Star 47 | Fork 48 |
49 |
50 | ); 51 | }; 52 | 53 | export default Header; 54 | -------------------------------------------------------------------------------- /src/docs/Content/Intro.jsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React from 'react'; 4 | 5 | import { 6 | codeInstallNpm 7 | } from './CodeSnippets'; 8 | 9 | const Intro = () => { 10 | return ( 11 | <> 12 |

Introduction

13 |
14 | 18 | 23 | 27 |
28 |

29 | React-power-tooltip is a powerful, fully customizable tooltip library. Besides the 30 | traditional tooltip purposes you can easily also use it as your popup or menu library: 31 |

32 |

33 |
    34 |
  1. 35 | Ready-to-use defaults: 36 | Choose between hoverable, static & alert tooltips. 37 |
  2. 38 |
  3. 39 | Fully customizable: 40 | Easily customize animations & styles to your wishes. 41 |
  4. 42 |
  5. 43 | Pixel perfect positioning: 44 | Easily align your tooltip to your target element. 45 |
  6. 46 |
  7. 47 | No dependencies: 48 | no side-strings attached. 49 |
  8. 50 |
51 |

52 | 53 |

54 | We are always open new ideas and improvements. Contribute on 55 | GitHub 56 | ! 57 |

58 |

Installation

59 |

Npm

60 |
61 |         {codeInstallNpm}
62 |       
63 | 64 | ); 65 | }; 66 | 67 | export default Intro; 68 | -------------------------------------------------------------------------------- /src/docs/Content/SideNav.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const sideNav = () => { 4 | return ( 5 |
6 | 19 |
20 | ); 21 | }; 22 | 23 | export default sideNav; 24 | -------------------------------------------------------------------------------- /src/docs/Content/ToggleCode.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ArrowUp from '../../assets/arrow-up.svg'; 3 | import ArrowDown from '../../assets/arrow-down.svg'; 4 | 5 | class ShowCode extends Component { 6 | state = { 7 | open: false 8 | } 9 | 10 | componentDidUpdate() { 11 | // eslint-disable-next-line 12 | Prism.highlightAll(); 13 | } 14 | 15 | clickHandler = () => { 16 | this.setState(prevState => ({ open: !prevState.open })); 17 | } 18 | 19 | render() { 20 | const { open } = this.state; 21 | const { children } = this.props; 22 | const SVG = open ? ArrowUp : ArrowDown; 23 | return ( 24 | <> 25 |
31 | {open ? 'Hide Code Example' : 'Show Code Example'} 32 | 37 |
38 | {open ? children : null} 39 | 40 | ); 41 | } 42 | } 43 | 44 | export default ShowCode; 45 | -------------------------------------------------------------------------------- /src/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React-power-tooltip | The ultimate tooltip library 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/docs/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | // eslint-disable-next-line 4 | import Prism from 'prismjs'; 5 | 6 | import Header from './Content/Header'; 7 | import SideNav from './Content/SideNav'; 8 | import Intro from './Content/Intro'; 9 | import BasicUse from './Content/BasicUse'; 10 | import AdvUse from './Content/AdvancedUse'; 11 | import Examples from './Content/Examples'; 12 | import Api from './Content/Api'; 13 | 14 | import './styles.css'; 15 | 16 | class Demo extends Component { 17 | componentDidMount() { 18 | Prism.highlightAll(); 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 |
25 |
26 |
27 | 28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | ); 39 | } 40 | } 41 | 42 | render(, document.getElementById('app')); 43 | -------------------------------------------------------------------------------- /src/docs/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | -webkit-tap-highlight-color: rgba(0,0,0,0); 4 | -moz-tap-highlight-color: rgba(0,0,0,0),; 5 | } 6 | 7 | *:focus { 8 | outline: none; 9 | } 10 | 11 | html, body { 12 | overflow-x: hidden; 13 | } 14 | 15 | body { 16 | background: #F5F6F7; 17 | font-family: 'Helvetica Neue',Helvetica; 18 | font-weight: 400; 19 | margin: 0; 20 | position: relative; 21 | width: 100%; 22 | } 23 | 24 | .flexContainer { 25 | display: flex; 26 | align-items: center; 27 | flex-direction: column; 28 | } 29 | 30 | .purpleGradient { 31 | background: linear-gradient(52deg, rgba(79,11,189,1) 0%, rgba(136,38,255,1) 100%, rgba(113,4,112,1) 100%); 32 | } 33 | 34 | pre, 35 | .greyBkgr { 36 | background: #ececec !important; 37 | } 38 | 39 | .greyBkgr>div { 40 | width: 95%; 41 | } 42 | 43 | 44 | pre { 45 | padding: 20px; 46 | text-align: left; 47 | width: 100%; 48 | overflow-x: auto !important; 49 | } 50 | 51 | .codeRow pre { 52 | width: 33%; 53 | height: 100%; 54 | overflow-y: hidden; 55 | display: inline-block; 56 | margin: 0; 57 | } 58 | 59 | .codeRow { 60 | width: 100%; 61 | background: #ececec; 62 | margin-top: 10px; 63 | display: flex; 64 | flex-direction: column; 65 | justify-content: center; 66 | padding: 10px 20px; 67 | } 68 | 69 | .codeRow>div { 70 | width: 100%; 71 | } 72 | 73 | .toggleCode { 74 | width: 100%; 75 | background: #ececec; 76 | cursor: pointer; 77 | padding: 20px; 78 | font-size: 14px; 79 | color: #686868; 80 | margin-top: 10px; 81 | } 82 | 83 | .header { 84 | justify-content: center; 85 | width: 100%; 86 | height: 380px; 87 | overflow: hidden; 88 | } 89 | 90 | .header h1 { 91 | color: white; 92 | font-size: 45px; 93 | word-break: normal; 94 | max-width: 80%; 95 | } 96 | 97 | .header h2 { 98 | color: #ececec; 99 | font-size: 25px; 100 | font-weight: 400; 101 | border-bottom: none; 102 | padding: 0; 103 | width: auto; 104 | max-width: 80%; 105 | } 106 | 107 | .ribbon { 108 | overflow: hidden; 109 | white-space: nowrap; 110 | } 111 | 112 | .ribbon a { 113 | border: 1px solid #e4e4e4; 114 | color: #fff; 115 | display: block; 116 | font: bold 81.25% 'Helvetica Neue', Helvetica, Arial, sans-serif; 117 | margin: 1px 0; 118 | padding: 10px 50px; 119 | text-align: center; 120 | text-decoration: none; 121 | position: absolute; 122 | right: -50px; 123 | top: 40px; 124 | transform: rotate(45deg); 125 | background-color: #383838; 126 | } 127 | 128 | .token.operator { 129 | background: none !important; 130 | } 131 | 132 | code { 133 | text-shadow: none !important; 134 | font-weight: bold; 135 | font-size: 14px; 136 | } 137 | 138 | section h1 { 139 | font-size: 18px; 140 | font-weight: 400; 141 | padding-top: 50px; 142 | margin: 0; 143 | } 144 | 145 | .firstH1 { 146 | padding-top: 75px; 147 | } 148 | 149 | h2 { 150 | border-bottom: 1px solid grey; 151 | font-size: 15px; 152 | font-weight: 400; 153 | padding: 10px 0; 154 | width: 100%; 155 | } 156 | 157 | h3 { 158 | font-size: 15px; 159 | font-weight: 400; 160 | padding: 10px 0; 161 | } 162 | 163 | .content { 164 | display: flex; 165 | flex-direction: row; 166 | width: 90%; 167 | max-width: 1200px; 168 | } 169 | 170 | .side { 171 | width: 30%; 172 | align-items: flex-start; 173 | padding: 50px 0; 174 | } 175 | 176 | .main { 177 | width: 100%; 178 | } 179 | 180 | section { 181 | width: 100%; 182 | } 183 | 184 | ul { 185 | list-style: none; 186 | line-height: 2; 187 | padding: 0; 188 | } 189 | 190 | li { 191 | cursor: pointer; 192 | } 193 | 194 | ul.subUl { 195 | padding: 0 15px; 196 | } 197 | 198 | .subList a { 199 | font-size: 14px; 200 | } 201 | 202 | a { 203 | text-decoration: none; 204 | font-size: 18px; 205 | color: inherit; 206 | } 207 | 208 | a:visited { 209 | color: inherit; 210 | } 211 | 212 | .sticky { 213 | position: -webkit-sticky; /* Safari */ 214 | position: sticky; 215 | top: 65px; 216 | display: none; 217 | } 218 | 219 | .button { 220 | display: flex; 221 | align-items: center; 222 | justify-content: center; 223 | padding: 5px; 224 | color: white; 225 | display: inline-block; 226 | cursor: pointer; 227 | } 228 | 229 | .innerButton { 230 | border: 1px solid #b8b8b8; 231 | padding: 20px 25px; 232 | cursor: pointer; 233 | } 234 | 235 | .hPosText { 236 | width: 70%; 237 | } 238 | 239 | .hPosText>div { 240 | width: calc(100%/3); 241 | display: inline-block; 242 | text-align: center; 243 | font-style: italic; 244 | } 245 | 246 | .row { 247 | display: flex; 248 | flex-direction: column; 249 | justify-content: space-around; 250 | } 251 | 252 | .row > div { 253 | margin-left: calc(50% - 85px); 254 | } 255 | 256 | .row > div:nth-child(2) { 257 | margin: 60px 0; 258 | margin-left: calc(50% - 85px); 259 | } 260 | 261 | .row.types > div:nth-child(2) { 262 | margin: 60px 0 110px 0; 263 | margin-left: calc(50% - 85px); 264 | } 265 | 266 | .row.animations > div { 267 | margin-left: 0px; 268 | align-self: center; 269 | } 270 | 271 | .row.v-pos-container > div:nth-child(2) p { 272 | margin-bottom: 40px; 273 | } 274 | 275 | .row.v-pos-container > div:nth-child(3) p { 276 | margin-bottom: 65px; 277 | } 278 | 279 | .row.h-pos-container > div { 280 | margin-left: 0px; 281 | align-self: center; 282 | } 283 | 284 | .row.h-pos-container > div:nth-child(2) { 285 | margin-top: 110px; 286 | } 287 | 288 | .row.h-pos-container > div:nth-child(3) { 289 | margin-top: 55px; 290 | margin-bottom: 40px; 291 | } 292 | 293 | .vPlaceHolder { 294 | margin-top: 0px; 295 | } 296 | 297 | table { 298 | font-family: arial, sans-serif; 299 | border-collapse: collapse; 300 | width: 100%; 301 | margin: 25px 0 5px 0; 302 | border-radius: 5px; 303 | overflow: hidden; 304 | } 305 | 306 | td, th { 307 | border: 1px solid #a1a1a1; 308 | text-align: left; 309 | padding: 10px; 310 | } 311 | 312 | td { 313 | font-size: 15px; 314 | } 315 | 316 | th { 317 | background: #444444; 318 | color: white; 319 | padding: 14px; 320 | } 321 | 322 | tr:nth-child(even) { 323 | background-color: #ececec; 324 | } 325 | 326 | .vPlaceHolder, 327 | .hPlaceHolder { 328 | height: 50px; 329 | width: 5px; 330 | font-size: 14px; 331 | background: purple; 332 | position: relative; 333 | } 334 | 335 | .hPlaceHolder { 336 | height: 5px; 337 | width: 50px; 338 | margin-top: 10px; 339 | } 340 | 341 | .colors .row>div { 342 | min-width: 150px; 343 | } 344 | 345 | .hoverDiv { 346 | width: 120px; 347 | height: 100px; 348 | display: flex; 349 | flex-direction: column; 350 | justify-content: center; 351 | align-items: center; 352 | position: relative; 353 | cursor: pointer; 354 | color: #2c2c2c; 355 | border: 2px solid #4b4b4b; 356 | background: rgba(137, 27, 211, 0.4); 357 | border-radius: 5px; 358 | } 359 | 360 | .square { 361 | position: relative; 362 | width: 250px; 363 | height: 250px; 364 | border: 2px solid #4b4b4b; 365 | overflow: hidden; 366 | background: rgba(137, 27, 211, 0.4); 367 | border-radius: 5px; 368 | } 369 | 370 | .adjInner { 371 | align-self: flex-start; 372 | padding-left: 20px; 373 | } 374 | 375 | .top, 376 | .left, 377 | .right, 378 | .bottom { 379 | position: absolute; 380 | height: calc(100% / 1.414); 381 | width: calc(100% / 1.414); 382 | top: 50%; 383 | left: 50%; 384 | border: 1px solid #4b4b4b; 385 | transform-origin: 0% 0%; 386 | cursor: pointer; 387 | } 388 | 389 | .right { 390 | transform: rotate(-45deg); 391 | } 392 | 393 | .topText, 394 | .bottomText, 395 | .rightText, 396 | .leftText { 397 | position: relative; 398 | top: 47.5%; 399 | left: 45%; 400 | } 401 | 402 | .leftText { 403 | left: -20%; 404 | } 405 | 406 | .topText { 407 | top: 20%; 408 | left: 45%; 409 | } 410 | 411 | .bottomText { 412 | top: 75%; 413 | left: 32% 414 | } 415 | 416 | .bottom { 417 | transform: rotate(45deg); 418 | } 419 | 420 | .top { 421 | transform: rotate(-135deg); 422 | } 423 | 424 | .left { 425 | transform: rotate(135deg); 426 | } 427 | 428 | .square > div:hover { 429 | background: rgba(128, 128, 128, 0.4); 430 | } 431 | 432 | .adjOuter { 433 | width: 400px !important; 434 | padding-left: 270px; 435 | } 436 | 437 | .tpPositions { 438 | width: 630px !important; 439 | padding-left: 280px; 440 | padding-top: 30px; 441 | padding-bottom: 30px; 442 | } 443 | 444 | .tableContainer { 445 | overflow: scroll; 446 | overflow-y: hidden; 447 | margin-bottom: 20px; 448 | } 449 | 450 | .github-btn-bar { 451 | margin-bottom: 50px; 452 | } 453 | 454 | .github-btn-bar span:nth-child(1) { 455 | margin-right: 10px; 456 | } 457 | 458 | .outer-hpos-container { 459 | padding-bottom: 110px; 460 | margin-top: 30px; 461 | } 462 | 463 | @keyframes rpt-slideUpDown { 464 | 0% { transform: translateY(20px); opacity: 0 } 465 | 100% { transform: translateY(0px); opacity: 1 } 466 | } 467 | 468 | @keyframes rpt-slideUpDown-out { 469 | 0% { transform: translateY(0px); opacity: 1 } 470 | 100% { transform: translateY(20px); opacity: 0 } 471 | } 472 | 473 | @media only screen and (min-width: 370px) { 474 | .header { 475 | height: 320px; 476 | } 477 | } 478 | 479 | @media only screen and (min-width: 400px) { 480 | .adjOuter { 481 | padding-left: 200px; 482 | } 483 | } 484 | 485 | @media only screen and (min-width: 460px) { 486 | .adjInnerContainer, 487 | .adjOuterContainer, 488 | .tpPositionsContainer { 489 | overflow-x: hidden !important; 490 | } 491 | 492 | .adjInner, 493 | .adjOuter { 494 | align-self: center; 495 | padding-left: 0; 496 | } 497 | 498 | .adjOuter { 499 | width: auto !important; 500 | } 501 | 502 | .tpPositions { 503 | width: auto !important; 504 | padding-left: 0px; 505 | align-self: center; 506 | } 507 | 508 | } 509 | 510 | @media only screen and (min-width: 500px) { 511 | .tableContainer { 512 | overflow-x: hidden; 513 | } 514 | } 515 | 516 | @media only screen and (min-width: 620px) { 517 | .header { 518 | height: 300px; 519 | } 520 | } 521 | 522 | @media only screen and (min-width: 768px) { 523 | html, body { 524 | overflow-x: visible; 525 | } 526 | .sticky { 527 | display: block; 528 | } 529 | .main, 530 | .content { 531 | width: 80%; 532 | } 533 | .row, 534 | .codeRow { 535 | flex-direction: row; 536 | } 537 | 538 | .row > div { 539 | margin-left: 0; 540 | } 541 | 542 | .row.types > div:nth-child(2), 543 | .row > div:nth-child(2), 544 | .row.h-pos-container > div:nth-child(2) { 545 | margin: 0; 546 | 547 | } 548 | 549 | .row.types > div:nth-child(3), 550 | .row > div:nth-child(3), 551 | .row.h-pos-container > div:nth-child(3) { 552 | margin: 0; 553 | } 554 | 555 | .row.types > div:nth-child(2) { 556 | margin-left: 15px; 557 | } 558 | 559 | .outer-hpos-container { 560 | padding-bottom: 150px; 561 | } 562 | 563 | } 564 | -------------------------------------------------------------------------------- /src/lib/Tooltip/Arrow.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; // 2 | 3 | const Arrow = ({ 4 | isHovered, 5 | hovBkg, 6 | bkgCol, 7 | flat 8 | }) => { 9 | const backgroundColor = isHovered ? hovBkg : bkgCol; 10 | const boxShadow = flat ? null : '0 0 0 1px rgba(0,0,0,.18)'; 11 | 12 | return ( 13 |
20 | ); 21 | }; 22 | 23 | export default Arrow; 24 | -------------------------------------------------------------------------------- /src/lib/Tooltip/TextBox.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class TextBox extends Component { 4 | state = { 5 | hoverIndex: null, 6 | firstH: null, 7 | lastH: null, 8 | totH: null 9 | } 10 | 11 | componentDidMount() { 12 | const heights = Object.keys(this.spanHeights) 13 | .map(key => this.spanHeights[key].clientHeight); 14 | const firstH = heights[0]; 15 | const lastH = heights[heights.length - 1]; 16 | const totH = heights.reduce( 17 | (accumulator, currentValue) => accumulator + currentValue, 0 18 | ); 19 | this.setState({ totH, firstH, lastH }); 20 | } 21 | 22 | unsetHover = () => { 23 | this.setState({ hoverIndex: null }); 24 | this.props.hoverArrow(false); 25 | } 26 | 27 | // Set & unset hover state 28 | onSpanHover = (index, lastIndex, numChildren) => { 29 | this.setState({ hoverIndex: index }); 30 | const { static: rctStatic, arw: arrow, pos: position, hoverArrow } = this.props; 31 | if (!rctStatic 32 | && ((index === 0 33 | && (position.isSide('bottom') || arrow.isAlign('v-start'))) 34 | || (index === lastIndex 35 | && (position.isSide('top') || arrow.isAlign('v-end'))) 36 | || numChildren === 1)) { 37 | return hoverArrow(true); 38 | } 39 | return hoverArrow(false); 40 | } 41 | 42 | render() { 43 | const { 44 | arw: arrow, 45 | pos: position, 46 | lines: lineSeparated, 47 | static: tpStatic, 48 | textBoxWidth: width, 49 | shadowColor: shCol, 50 | shadowShape: shShape, 51 | move, 52 | backgroundColor, 53 | padding, 54 | borderRadius, 55 | hoverBackground, 56 | hoverColor, 57 | alert, 58 | flat, 59 | children 60 | } = this.props; 61 | 62 | const { 63 | hoverIndex, 64 | totH, 65 | firstH, 66 | lastH 67 | } = this.state; 68 | 69 | const calcHPos = (left, center, right) => { 70 | return position.isAlign('center') 71 | ? center : position.isAlign('left') ? left : right; 72 | }; 73 | const calcVPos = (perc, elHeight, divider, adjMove, totHeight) => { 74 | return `calc(${perc}% - ${totHeight || 0}px - ${elHeight}px/${divider} + ${adjMove || 0}px)`; 75 | }; 76 | // TODO: REfactor 77 | const calcTopPos = (elHeight, totHeight) => { 78 | if (position.align === 'center') { 79 | return calcVPos(50, elHeight, 2, null, totHeight); 80 | } 81 | if (position.align === 'bottom') { 82 | return calcVPos(100, elHeight, 2, -12, totHeight); 83 | } 84 | return calcVPos(0, elHeight, 2, 12, totHeight); 85 | }; 86 | 87 | const numberChildren = React.Children.count(children); 88 | const lastIndex = numberChildren - 1; 89 | this.spanHeights = {}; 90 | 91 | const adjChildren = React.Children.map(children, (child, index) => { 92 | const style = { 93 | backgroundColor, 94 | padding 95 | }; 96 | if (width === 'auto') style.whiteSpace = 'nowrap'; 97 | if (!tpStatic && hoverIndex === index) { 98 | style.color = hoverColor; 99 | style.backgroundColor = hoverBackground; 100 | } 101 | if (lineSeparated && lastIndex !== index) { 102 | style.borderBottom = lineSeparated; 103 | } 104 | 105 | let ref = null; 106 | // eslint-disable-next-line 107 | ref = span => this.spanHeights[`span${index + 1}`] = span; 108 | 109 | const childProps = { 110 | ...child.props, 111 | ref, 112 | style, 113 | onMouseOver: () => this.onSpanHover(index, lastIndex, numberChildren) 114 | }; 115 | return React.cloneElement(child, childProps); 116 | }); 117 | 118 | let left = ''; 119 | let right = ''; 120 | let top = '8px'; 121 | // Align: left, center, right 122 | const hLeftPos = calcHPos('100% - 50px', '50% - 40px', '0% - 30px'); 123 | const hRightPos = calcHPos('0% - 30px', '50% - 40px', '100% - 50px'); 124 | 125 | switch (arrow.position) { 126 | case 'h-start': 127 | left = `calc(${hRightPos})`; 128 | break; 129 | case 'h-end': 130 | right = `calc(${hLeftPos})`; 131 | break; 132 | case 'v-start': 133 | top = calcTopPos(firstH, null); 134 | break; 135 | case 'v-end': 136 | top = calcTopPos(lineSeparated ? -lastH + 1 : -lastH, totH); 137 | break; 138 | case 'v-center': 139 | top = `calc(0% - ${totH}px/2 + 11px)`; 140 | if (position.isAlign('center')) { 141 | top = `calc(50% - ${totH}px/2)`; 142 | } 143 | if (position.isAlign('bottom')) { 144 | top = `calc(100% - ${totH}px/2 - 11px)`; 145 | } 146 | break; 147 | default: 148 | break; 149 | } 150 | 151 | switch (position.side) { 152 | case 'top': 153 | top = calcVPos(0, totH, 1, 13); 154 | break; 155 | case 'left': 156 | right = '8px'; 157 | break; 158 | case 'right': 159 | left = '8px'; 160 | break; 161 | default: 162 | break; 163 | } 164 | 165 | let textBoxWidth = width; 166 | 167 | if (textBoxWidth !== 'auto') { 168 | textBoxWidth = Number(width.slice(0, -2)); 169 | if (move.left > 0) textBoxWidth += move.left; 170 | if (move.right > 0) textBoxWidth += move.right; 171 | } 172 | 173 | const boxStyle = { 174 | left, 175 | right, 176 | top, 177 | width: textBoxWidth, 178 | borderRadius 179 | }; 180 | 181 | const shColAdj = shCol.substr(0, shCol.lastIndexOf(',')).replace(/[)]/g, ','); 182 | const shadow = `${shShape} ${shCol}, 0 0 3px ${shColAdj}, 0.1), 0 0 0 1px ${shColAdj}, 0.15)`; 183 | const boxShadow = flat ? null : shadow; 184 | const alertStyle = alert ? 'rpt-alert' : ''; 185 | const rgb = alert || 'rgb(248, 109, 109)'; 186 | const alertShadow = alert ? `0 0 0 ${rgb.slice(0, rgb.length - 1)}, 0.4)` : null; 187 | const noNeg = number => number > 0 ? number : 0; 188 | 189 | return ( 190 |
199 |
208 |
216 |
223 | {adjChildren} 224 |
225 |
226 |
227 | ); 228 | } 229 | } 230 | 231 | export default TextBox; 232 | -------------------------------------------------------------------------------- /src/lib/Tooltip/styles.jsx: -------------------------------------------------------------------------------- 1 | const cssRules = ` 2 | .rpt-container { 3 | position: absolute; 4 | display: flex; 5 | } 6 | 7 | .rpt-bottom, 8 | .rpt-top { 9 | justify-content: center; 10 | width: 100%; 11 | } 12 | 13 | .rpt-right, 14 | .rpt-align-left { 15 | justify-content: flex-start; 16 | } 17 | 18 | .rpt-left, 19 | .rpt-align-right { 20 | justify-content: flex-end; 21 | } 22 | 23 | .rpt-align-center { 24 | align-items: center; 25 | } 26 | 27 | .rpt-align-bottom { 28 | align-items: flex-end; 29 | } 30 | 31 | .rpt-textbox { 32 | position: relative; 33 | z-index: 2; 34 | } 35 | 36 | .rpt-textbox span { 37 | display: block; 38 | cursor: text; 39 | box-sizing: border-box; 40 | } 41 | 42 | .rpt-textbox span p { 43 | font-size: 90%; 44 | font-weight: normal; 45 | line-height: 12px; 46 | color: inherit; 47 | opacity: 0.6; 48 | padding: 0; 49 | margin: 0; 50 | margin-top: 6px; 51 | } 52 | 53 | .rpt-hover span { 54 | cursor: pointer; 55 | } 56 | 57 | .rpt-shadow-container { 58 | width: 100%; 59 | height: 100%; 60 | z-index: 0; 61 | } 62 | 63 | .rpt-shadow-container, 64 | .rpt-textbox-container { 65 | position: absolute; 66 | animation: none; 67 | } 68 | 69 | .rpt-arrow { 70 | transform: rotate(45deg); 71 | width: 15px; 72 | height: 15px; 73 | margin:3px; 74 | z-index: 1; 75 | } 76 | 77 | .rpt-alert { 78 | animation: rpt-alert 2s infinite; 79 | animation-fill-mode: initial; 80 | } 81 | 82 | @keyframes rpt-alert { 83 | 70% {box-shadow: 0 0 0 10px rgba(255, 255, 255, 0)} 84 | 100% {box-shadow: 0 0 0 10px rgba(255, 255, 255, 0)} 85 | } 86 | 87 | @keyframes rpt-bounce { 88 | 0% {transform: scale(0.8); opacity: 0.1} 89 | 60% {transform: scale(1.1); opacity: 1} 90 | 100% {transform: scale(1);} 91 | } 92 | 93 | @keyframes rpt-bounce-out { 94 | 20% { transform: scale(1.1); } 95 | 100% { transform: scale(0.8); opacity: 0.1 } 96 | } 97 | 98 | @keyframes rpt-fade { 99 | 0% { opacity: 0.1} 100 | 100% { opacity: 1} 101 | } 102 | 103 | @keyframes rpt-fade-out { 104 | 0% { opacity: 1 } 105 | 60% { opacity: 0 } 106 | 100% { opacity: 0} 107 | } 108 | `; 109 | 110 | export default cssRules; 111 | -------------------------------------------------------------------------------- /src/lib/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import TextBox from './Tooltip/TextBox'; 3 | import Arrow from './Tooltip/Arrow'; 4 | 5 | import cssRules from './Tooltip/styles'; 6 | 7 | class Tooltip extends Component { 8 | state = { 9 | hoverArrow: false, 10 | show: this.props.show, 11 | mount: true, 12 | hasInitialized: false 13 | } 14 | 15 | componentWillMount() { 16 | // Injecting styles directly into header 17 | if (!document.getElementById('rpt-css')) { 18 | const $style = document.createElement('style'); 19 | $style.type = 'text/css'; 20 | $style.id = 'rpt-css'; 21 | document.head.appendChild($style); 22 | $style.innerHTML = cssRules; 23 | } 24 | // Basic prop type checking 25 | Object.keys(this.props).forEach((propName) => { 26 | const type = typeof this.props[propName]; 27 | const text = `React-power-tooptip: [${propName}] prop should be a`; 28 | if (propName !== 'children' && type !== 'boolean' && type !== 'string') { 29 | // eslint-disable-next-line 30 | console.error(`${text} string (check also units)`); 31 | } 32 | }); 33 | } 34 | 35 | shouldComponentUpdate(nextProps, nextState) { 36 | return nextProps !== this.props 37 | || nextState.hasInitialized !== this.state.hasInitialized 38 | || nextState.mount !== this.state.mount 39 | || nextState.hoverArrow !== this.state.hoverArrow; 40 | } 41 | 42 | componentDidUpdate() { 43 | /* eslint-disable */ 44 | if (!this.state.hasInitialized) this.setState({ show: this.props.show, hasInitialized: true }); 45 | if (this.props.show) this.setState({ mount: true }); 46 | if (!this.props.animation) this.setState({ mount: false }); 47 | /* eslint-disable */ 48 | } 49 | 50 | hoverArrow = (bool) => { 51 | this.setState({ hoverArrow: bool }); 52 | } 53 | 54 | render() { 55 | const { 56 | lineSeparated: lines, 57 | position: pos, 58 | hoverBackground, 59 | backgroundColor, 60 | arrowAlign: arwAlign, 61 | moveDown, 62 | moveRight, 63 | moveLeft, 64 | moveUp, 65 | textAlign, 66 | fontFamily, 67 | fontWeight, 68 | fontSize, 69 | color, 70 | animation, 71 | zIndex, 72 | show, 73 | flat 74 | } = this.props; 75 | 76 | // Sets if false no line; if true default line; if string custom line; 77 | const lineSeparated = typeof (lines) === 'boolean' 78 | ? '1px solid #ececec' : lines; 79 | 80 | function isAlign(str) { 81 | return this.align ? this.align === str 82 | : this.position === str; 83 | } 84 | 85 | function isSide(str) { 86 | return this.side === str; 87 | } 88 | 89 | const position = { 90 | side: pos.split(' ')[0], 91 | align: pos.split(' ')[1], 92 | isAlign, 93 | isSide 94 | }; 95 | 96 | const arrow = { 97 | isAlign, 98 | position: arwAlign 99 | }; 100 | 101 | const { side, align } = position; 102 | const classes = ['rpt-container']; 103 | let tooltipStyle = {}; 104 | let bottom; 105 | 106 | const arrange = (top, left, right, height, width, cssSel) => { 107 | tooltipStyle = { top, left, right, height, width }; 108 | classes.push(cssSel); 109 | }; 110 | 111 | switch (side) { 112 | case 'bottom': 113 | arrange('100%', '0px', '', '', '100%', 'rpt-bottom'); 114 | break; 115 | case 'top': 116 | arrange('', '0px', '', '', '100%', 'rpt-top'); 117 | bottom = '100%'; 118 | break; 119 | case 'right': 120 | arrange('0px', '100%', '', '100%', '', 'rpt-right'); 121 | break; 122 | default: 123 | arrange('0px', '', '100%', '100%', '', 'rpt-left'); 124 | break; 125 | } 126 | 127 | const onAxis = { 128 | y: position.isSide('top') || position.isSide('bottom'), 129 | x: position.isSide('left') || position.isSide('right') 130 | }; 131 | 132 | arrow.position = onAxis.y ? `h-${arrow.position}` : `v-${arrow.position}`; 133 | 134 | const num = str => Number(str.slice(0, -2)); 135 | const move = { 136 | down: num(moveDown), 137 | up: num(moveUp), 138 | left: num(moveLeft), 139 | right: num(moveRight) 140 | }; 141 | 142 | const oneMovePropIsNeg = move.down < 0 || move.up < 0 143 | || move.left < 0 || move.right < 0; 144 | 145 | switch (align) { 146 | case 'left': 147 | if (onAxis.y) classes.push('rpt-align-left'); 148 | break; 149 | case 'right': 150 | if (onAxis.y) classes.push('rpt-align-right'); 151 | break; 152 | case 'bottom': 153 | if (onAxis.x) classes.push('rpt-align-bottom'); 154 | break; 155 | case 'top': 156 | break; 157 | default: 158 | if (onAxis.x) { 159 | classes.push('rpt-align-center'); 160 | if (!oneMovePropIsNeg) { 161 | move.down *= 2; 162 | move.up *= 2; 163 | } 164 | } 165 | if (onAxis.y && !oneMovePropIsNeg) { 166 | move.right *= 2; 167 | move.left *= 2; 168 | } 169 | break; 170 | } 171 | 172 | const adjustment = `${move.down}px ${move.left}px ${move.up}px ${move.right}px`; 173 | 174 | tooltipStyle = { 175 | ...tooltipStyle, 176 | zIndex, 177 | color, 178 | bottom, 179 | fontSize, 180 | textAlign, 181 | fontFamily, 182 | fontWeight, 183 | padding: oneMovePropIsNeg ? null : adjustment, 184 | margin: oneMovePropIsNeg ? adjustment : null, 185 | animation: show ? `rpt-${animation} 0.2s` : `rpt-${animation}-out 0.15s` 186 | }; 187 | 188 | return ((!animation && show) || (this.state.show && this.state.mount)) ? ( 189 |
{ if (!show && animation) this.setState({ mount: false }) }} 193 | > 194 |
200 | 206 | 214 |
215 |
216 | ) : null; 217 | } 218 | } 219 | 220 | // Specifies the default values for props: 221 | Tooltip.defaultProps = { 222 | hoverBackground: '#ececec', 223 | hoverColor: 'black', 224 | backgroundColor: 'white', 225 | textBoxWidth: '150px', 226 | padding: '15px 20px', 227 | borderRadius: '5px', 228 | shadowColor: 'rgba(0,0,0,0.251)', 229 | shadowShape: '0 8px 15px', 230 | moveDown: '0px', 231 | moveRight: '0px', 232 | moveLeft: '0px', 233 | moveUp: '0px', 234 | position: 'right center', 235 | arrowAlign: 'start', 236 | textAlign: 'left', 237 | fontFamily: 'inherit', 238 | fontWeight: 'bold', 239 | fontSize: 'inherit', 240 | color: 'inherit', 241 | zIndex: '100', 242 | animation: '' 243 | }; 244 | 245 | export default Tooltip; 246 | -------------------------------------------------------------------------------- /webpack/dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: path.join(__dirname, '/../src/docs'), 6 | output: { 7 | path: path.join(__dirname, '/../docs'), 8 | filename: 'bundle.js' 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.(js|jsx)$/, 14 | use: 'babel-loader', 15 | exclude: /node_modules/ 16 | }, 17 | { 18 | test: /\.css$/, 19 | use: ['style-loader', 'css-loader'] 20 | }, 21 | { 22 | test: /\.(png|jpg|gif|svg|ico)$/, 23 | use: [ 24 | { 25 | loader: 'file-loader', 26 | options: {} 27 | } 28 | ] 29 | } 30 | ] 31 | }, 32 | plugins: [ 33 | new HtmlWebpackPlugin({ 34 | template: path.join(__dirname, '/../src/docs/index.html'), 35 | favicon: path.join(__dirname, '/../src/assets/favicon.png') 36 | }) 37 | ], 38 | resolve: { 39 | extensions: ['.js', '.jsx'] 40 | }, 41 | devServer: { 42 | contentBase: path.join(__dirname, '/../docs'), 43 | port: 8000, 44 | stats: 'minimal' 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /webpack/prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: path.join(__dirname, '/../src/docs'), 6 | output: { 7 | path: path.join(__dirname, '/../docs'), 8 | filename: 'bundle.js' 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.(js|jsx)$/, 14 | use: 'babel-loader', 15 | exclude: /node_modules/ 16 | }, 17 | { 18 | test: /\.css$/, 19 | use: ['style-loader', 'css-loader'] 20 | }, 21 | { 22 | test: /\.(png|jpg|gif|svg|ico)$/, 23 | use: [ 24 | { 25 | loader: 'file-loader', 26 | options: {} 27 | } 28 | ] 29 | } 30 | ] 31 | }, 32 | plugins: [ 33 | new HtmlWebpackPlugin({ 34 | template: path.join(__dirname, '/../src/docs/index.html'), 35 | favicon: path.join(__dirname, '/../src/assets/favicon.png') 36 | }) 37 | ], 38 | resolve: { 39 | extensions: ['.js', '.jsx'] 40 | }, 41 | devServer: { 42 | contentBase: path.join(__dirname, '/../docs'), 43 | port: 8000, 44 | stats: 'minimal' 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /webpack/test.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: path.join(__dirname, '/../puppeteer/input'), 6 | output: { 7 | path: path.join(__dirname, '/../puppeteer'), 8 | filename: 'bundle.js' 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.(js|jsx)$/, 14 | use: 'babel-loader', 15 | exclude: /node_modules/ 16 | }, 17 | { 18 | test: /\.css$/, 19 | use: ['style-loader', 'css-loader'] 20 | } 21 | ] 22 | }, 23 | plugins: [ 24 | new HtmlWebpackPlugin({ 25 | template: path.join(__dirname, '/../puppeteer/input/index.html') 26 | }) 27 | ], 28 | resolve: { 29 | extensions: ['.js', '.jsx'] 30 | } 31 | }; 32 | --------------------------------------------------------------------------------