Skip to content

Instantly share code, notes, and snippets.

@mik01aj
Last active April 21, 2017 13:02
Show Gist options
  • Save mik01aj/5b98bad5b5ba04a1b93f to your computer and use it in GitHub Desktop.
Save mik01aj/5b98bad5b5ba04a1b93f to your computer and use it in GitHub Desktop.

Revisions

  1. mik01aj revised this gist Aug 20, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ That's why I created 2 helpers to use Tether with React.

    The first one, `TetheredElement` is a plain JS helper to create a new element, attach it to some other one via Tether, and populate it with some React component.

    The second one, `TetherTarget` is a React component and it uses `TetheredElement` to integrate it further with React, so that you can attach components to each other with Tether, by writing just:
    The second one, `TetherTarget` is a React component and it uses `TetheredElement` to integrate it further with React, so that you can attach components to each other with Tether, without leaving the cozy React/JSX world and worrying about manual DOM operations. Just write:

    ```jsx
    var tetherOptions = {
  2. mik01aj revised this gist Aug 20, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    [Tether](http://github.hubspot.com/tether/) is a great library for positioning stuff (tooltips, modals, hints, etc) in your web app.

    But, as I use React, it was pretty problematic for me, as Tether [mutates the DOM](https://github.com/HubSpot/tether/issues/54) and React breaks miserably when it sees mutated DOM.
    But, as I use React, it was pretty problematic for me, as Tether [mutates the DOM](https://github.com/HubSpot/tether/issues/54) and React breaks miserably when it sees mutated DOM. The solution is to have the tethered element outside the part of the DOM tree which is controlled by React (in this case, I use `document.body`).

    That's why I created 2 helpers to use Tether with React.

  3. mik01aj revised this gist Aug 20, 2015. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions TetherTarget.jsx
    Original file line number Diff line number Diff line change
    @@ -19,8 +19,7 @@ var TetherTarget = React.createClass({
    var tetherOptions = _.merge({
    target: this.getDOMNode(),
    }, this.props.tetherOptions);
    var tetheredComponent = <div style={ {zIndex: 1000} }>{ this.props.tethered }</div>;
    this.tethered = new TetheredElement(tetheredComponent, tetherOptions);
    this.tethered = new TetheredElement(this.props.tethered, tetherOptions);
    },
    componentWillUnmount: function () {
    this.tethered.destroy();
  4. mik01aj revised this gist Aug 20, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -8,7 +8,7 @@ The first one, `TetheredElement` is a plain JS helper to create a new element, a

    The second one, `TetherTarget` is a React component and it uses `TetheredElement` to integrate it further with React, so that you can attach components to each other with Tether, by writing just:

    ```
    ```jsx
    var tetherOptions = {
    // element and target are set automatically
    attachment: 'top center',
  5. mik01aj revised this gist Aug 20, 2015. 2 changed files with 3 additions and 5 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ That's why I created 2 helpers to use Tether with React.

    The first one, `TetheredElement` is a plain JS helper to create a new element, attach it to some other one via Tether, and populate it with some React component.

    The second one, `TetherTarget`, uses the first one to integrate it further with React. With it, you can attach components to each other with Tether, by writing just:
    The second one, `TetherTarget` is a React component and it uses `TetheredElement` to integrate it further with React, so that you can attach components to each other with Tether, by writing just:

    ```
    var tetherOptions = {
    @@ -17,7 +17,7 @@ var tetherOptions = {
    ],
    };
    <TetherTarget tethered={ <i>I'm tethered!</i> } tetherOptions={ tetherOptions }>
    <h1>Hello world! I'm a big box!</h1>
    <h1>Hello world! I'm a big box, which is the Tether target!</h1>
    </TetherTarget>
    ```

    4 changes: 1 addition & 3 deletions TetheredElement.js
    Original file line number Diff line number Diff line change
    @@ -2,14 +2,12 @@

    var _ = require('lodash');
    var React = require('react');
    var Tether = global.IN_BROWSER && require('tether-browserify/tether'); // browser only
    var Tether = require('tether-browserify/tether');


    // NOTE: This is not a React component. It's a plain JS object that manages a React component.

    function TetheredElement(reactComponent, tetherOptions) {
    console.assert(global.IN_BROWSER);

    this.reactComponent = reactComponent;

    this.domNode = document.createElement('div');
  6. mik01aj created this gist Aug 20, 2015.
    24 changes: 24 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    [Tether](http://github.hubspot.com/tether/) is a great library for positioning stuff (tooltips, modals, hints, etc) in your web app.

    But, as I use React, it was pretty problematic for me, as Tether [mutates the DOM](https://github.com/HubSpot/tether/issues/54) and React breaks miserably when it sees mutated DOM.

    That's why I created 2 helpers to use Tether with React.

    The first one, `TetheredElement` is a plain JS helper to create a new element, attach it to some other one via Tether, and populate it with some React component.

    The second one, `TetherTarget`, uses the first one to integrate it further with React. With it, you can attach components to each other with Tether, by writing just:

    ```
    var tetherOptions = {
    // element and target are set automatically
    attachment: 'top center',
    constraints: [
    {to: 'window', pin: true, attachment: 'together'},
    ],
    };
    <TetherTarget tethered={ <i>I'm tethered!</i> } tetherOptions={ tetherOptions }>
    <h1>Hello world! I'm a big box!</h1>
    </TetherTarget>
    ```

    Comments are welcome :)
    39 changes: 39 additions & 0 deletions TetherTarget.jsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    /** @jsx React.DOM */
    'use strict';

    var _ = require('lodash');
    var React = require('react');

    var TetheredElement = require('./TetheredElement');


    var TetherTarget = React.createClass({
    propTypes: {
    tethered: React.PropTypes.node.isRequired,
    tetherOptions: React.PropTypes.object.isRequired
    },
    getInitialState: function () {
    return {tooltipVisible: false};
    },
    componentDidMount: function () {
    var tetherOptions = _.merge({
    target: this.getDOMNode(),
    }, this.props.tetherOptions);
    var tetheredComponent = <div style={ {zIndex: 1000} }>{ this.props.tethered }</div>;
    this.tethered = new TetheredElement(tetheredComponent, tetherOptions);
    },
    componentWillUnmount: function () {
    this.tethered.destroy();
    },
    componentDidUpdate: function () {
    this.tethered.update();
    },
    render: function () {
    var divProps = _.omit(this.props, ['tethered', 'tetherOptions']);
    return <div {... divProps }>
    { this.props.children }
    </div>;
    },
    });

    module.exports = TetherTarget;
    38 changes: 38 additions & 0 deletions TetheredElement.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    'use strict';

    var _ = require('lodash');
    var React = require('react');
    var Tether = global.IN_BROWSER && require('tether-browserify/tether'); // browser only


    // NOTE: This is not a React component. It's a plain JS object that manages a React component.

    function TetheredElement(reactComponent, tetherOptions) {
    console.assert(global.IN_BROWSER);

    this.reactComponent = reactComponent;

    this.domNode = document.createElement('div');
    this.domNode.style.position = 'absolute'; // needed for Tether
    document.body.appendChild(this.domNode);

    this.tether = new Tether(_.merge({
    element: this.domNode,
    }, tetherOptions));

    this.update();
    }
    TetheredElement.prototype.update = function () {
    React.render(
    this.reactComponent,
    this.domNode,
    () => this.tether.position()
    );
    };
    TetheredElement.prototype.destroy = function () {
    React.unmountComponentAtNode(this.domNode);
    this.domNode.parentNode.removeChild(this.domNode);
    this.tether.destroy();
    };

    module.exports = TetheredElement;