cytoscape / cytoscape.js-qtip

A Cytoscape.js extension that wraps the QTip jQuery library
MIT License
42 stars 35 forks source link

qtip.$domEle.qtip( opts ) is not a function, again. #28

Closed ssmereka closed 5 years ago

ssmereka commented 7 years ago

I'm using webpack & react and getting an error where qtip.$domEle.qtip( opts ); is not a function. I have seen a similar issue #22 where the solution was to require jquery before registering the crytoscape-qtip extension. However, this did not fix the issue. After further investigation I noticed that you added this #hack to the cytoscape-qtip library already.

I then tried just passing in jQuery directly to cytoscape-qtip and had some success, the register methods became the following:

Webpack:

plugins: [
  new webpack.ProvidePlugin({
    '$': 'jquery',
    'jQuery': 'jquery',
    'window.jQuery': 'jquery',
    'window.$': 'jquery'
  })
],

My App:

import cytoscape from 'cytoscape'
import qtip from 'qtip2'
import cyqtip from 'cytoscape-qtip'
cyqtip(cytoscape, window.jQuery)

renderCytoscape = (graphData) => {
    cy.on('tap', 'node', function(event) {
        event.target.qtip({
            content: 'this is content'
        }, event)
    })
}

cytoscape-qtip:

if( typeof module !== 'undefined' && module.exports ){ // expose as a commonjs module
    module.exports = function( cytoscape, jQuery ){
        register( cytoscape, jQuery );
    };
} else if( typeof define !== 'undefined' && define.amd ){ // expose as an amd/requirejs module
    define('cytoscape-qtip', function(){
        return register;
    });
}

After this the position of the tooltip when I select a node is correct, I can see the hidden div. However, the qtip tooltip will be displayed offscreen or sometimes is not generated at all.

screen shot 2017-05-08 at 4 38 39 pm
maxkfranz commented 7 years ago

It's all automatic in 3.x if you use a modern setup like npm/browserify or npm/webpack. You can just do

import cytoscape from 'cytoscape';
import qtip from 'cytoscape-qtip';

cytoscape.use( qtip );

http://js.cytoscape.org/#extensions

Someone on the team made a test page for extensions using npm/webpack. It worked fine with .use().

Your webpack config is wrong. You either use globals or you register with .use(). You don't do both. As the readme says, it's all automatic if you use globals.

After this the position of the tooltip when I select a node is correct, I can see the hidden div. However, the qtip tooltip will be displayed offscreen or sometimes is not generated at all.

You haven't done anything to create/show the qtip. You've just set a config. Set an event in the config and immediately emit it manually or just specify the events in the config as the qtip api expects: http://qtip2.com/options#show

ssmereka commented 7 years ago

Thanks for you helpful advice and quick reply, you are awesome!

Turns out the problem was related to the jQuery version. I was using jQuery version 3.1.1 and once I downgrading to version 2 everything worked.

Thanks @CMaylone for determining this was the issue.

maxkfranz commented 7 years ago

Unfortunately I don't think qtip2 supports jquery@3.

We'd like to replace this qtip extension with extensions for Popper and Tippy but that GSOC project wasn't accepted and I don't have time right now to make those myself. It should be relatively straightforward as the Popper author added support for creating Poppers without DOM elements. I've also asked the Tippy author about it also.

@josephst Would you be interested in creating Popper and Tippy extensions?

josephst commented 7 years ago

@maxkfranz I'll start with the Popper extension; hopefully it can be partially reused when Tippy gains support for non-DOM elements. I've got a relatively busy summer but having the qtip extension available for reference should help move things along.

Plan is to use bounding boxes calculated by Cytoscape.js to create a referenceObject for Popper.js based on whichever element(s) the function is called on and have two parameters for the function: one for content, one for tooltip options. I expect that the extension will have to specify some Cytoscape.js-specific options (and disallow some options) to go between Cytoscape.js and Popper. A lot of the showing/ hiding/ panning and zooming implementation should be similar to how it's done in the qTip extension.

I'll get a new repo going for this extension and discussion once I've started working on it.

maxkfranz commented 7 years ago

Plan is to use bounding boxes calculated by Cytoscape.js to create a referenceObject for Popper.js based on whichever element(s) the function is called on and have two parameters for the function: one for content, one for tooltip options.

Yes, then I'm hoping that the Popper extension can be used to make a Tippy extension much easier. If Tippy allows for passing a Popper instance, then the Tippy extension basically just needs to pass in the result of the Popper extension. That way, we have a great extension for general overlay content (Popper) and a great extension specifically for tooltips (Tippy).

I expect that the extension will have to specify some Cytoscape.js-specific options (and disallow some options) to go between Cytoscape.js and Popper.

It might be good to put the Cytoscape-specific options in a separate object to keep things clean.

finger563 commented 7 years ago

I'm also willing to work on this.

maxkfranz commented 7 years ago

@finger563, could you co-ordinate with @josephst? Since Joseph is starting on the Popper extension first, maybe it would make sense for you to work on https://github.com/atomiks/tippyjs/issues/40 in parallel?

finger563 commented 7 years ago

@maxkfranz, sure I'll take a look into that as well. Right now I'm getting ctyoscapejs-popper up and running in my own fork but I'll try to work on that as well :)

josephst commented 7 years ago

@finger563 progress on the extension has been pretty slow so far owing to not having a lot of time to work on it beside weekends so you're welcome to work on it too. I can see that you've got a fork of my project going; I was able to get some work done this weekend to get Popper working on graph elements but it still needs to be extended to work on the whole graph (core) and reposition with graph changes. After that, there needs to be a way to pass options to Popper.js (and possibly some default values to integrate it nicely with Cytoscape.js).

On the topic of changing position, so far the only method I've found is to repeatedly destroy and recreate the tooltip with a new ReferenceObject, but I'm pretty sure this just means I'm missing something obvious in the documentation for Popper.

maxkfranz commented 7 years ago

@josephst @finger563 You should use getter functions to make normally static properties dynamic:

josephst commented 7 years ago

@maxkfranz I've got some more work done on the extension (https://github.com/josephst/cytoscapejs-popper). Right now it's able to display a popper on individual graph elements, as well as the entire graph (i.e. core). I took out the showing/ hiding code since I think this is outside the scope of Popper.js (more of a Tooltip.js thing).

The extension accepts a single object for its options; the object currently has target and popper keys. The target key is capable of accepting either a string or function as its value (functions are called with the element or graph as their first argument and should produce a string) and corresponds to the ID of the element to use as a popper. The popper value is another object which is passed to Popper.js to modify tooltip behavior.

I still need to adjust the popper positioning some (they keep overlapping nodes) but so far things are going relatively well; progress is helped by the fact that Popper.js is more limited in scope than the qTip extension; i.e. don't have to worry about creating HTML elements from strings or showing/ hiding poppers.

maxkfranz commented 7 years ago

It sounds like you're on the right track. The popper extension should be as thin as possible, focussing on positioning/dimensions. I'll give feedback on the code on Tuesday

On Jul 2, 2017, at 19:19, Joseph Stahl notifications@github.com wrote:

@maxkfranz I've got some more work done on the extension (https://github.com/josephst/cytoscapejs-popper). Right now it's able to display a popper on individual graph elements, as well as the entire graph (i.e. core). I took out the showing/ hiding code since I think this is outside the scope of Popper.js (more of a Tooltip.js thing).

The extension accepts a single object for its options; the object currently has target and popper keys. The target key is capable of accepting either a string or function as its value (functions are called with the element or graph as their first argument and should produce a string) and corresponds to the ID of the element to use as a popper. The popper value is another object which is passed to Popper.js to modify tooltip behavior.

I still need to adjust the popper positioning some (they keep overlapping nodes) but so far things are going relatively well; progress is helped by the fact that Popper.js is more limited in scope than the qTip extension; i.e. don't have to worry about creating HTML elements from strings or showing/ hiding poppers.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

maxkfranz commented 7 years ago

Hi Joseph:

Here's some feedback on your extension. It looks good! I think the only thing that I would change in it is that we don't need to be driving position updates from the cyjs side, like we do in the qtip extension. I think the client/popper should pull updates from our dynamic spec object. As long as the client gets the popper object back, they can call popper.scheduleUpdate() whenever they want.

I know the tippy lib does polling to update the tooltip position on changes, so as long as the popper object spec is dynamic like you have it, then it should work fine with tippy.

Thanks,

-Max

On Sun, Jul 2, 2017 at 7:54 PM, Max Franz maxkfranz@gmail.com wrote:

It sounds like you're on the right track. The popper extension should be as thin as possible, focussing on positioning/dimensions. I'll give feedback on the code on Tuesday

On Jul 2, 2017, at 19:19, Joseph Stahl notifications@github.com wrote:

@maxkfranz https://github.com/maxkfranz I've got some more work done on the extension (https://github.com/josephst/cytoscapejs-popper). Right now it's able to display a popper on individual graph elements, as well as the entire graph (i.e. core). I took out the showing/ hiding code since I think this is outside the scope of Popper.js (more of a Tooltip.js thing).

The extension accepts a single object for its options; the object currently has target and popper keys. The target key is capable of accepting either a string or function as its value (functions are called with the element or graph as their first argument and should produce a string) and corresponds to the ID of the element to use as a popper. The popper value is another object which is passed to Popper.js to modify tooltip behavior.

I still need to adjust the popper positioning some (they keep overlapping nodes) but so far things are going relatively well; progress is helped by the fact that Popper.js is more limited in scope than the qTip extension; i.e. don't have to worry about creating HTML elements from strings or showing/ hiding poppers.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/cytoscape/cytoscape.js-qtip/issues/28#issuecomment-312523035, or mute the thread https://github.com/notifications/unsubscribe-auth/AA8Xc8ey-aTGZ0j_TEQzzbjTJOMuI3lWks5sKCV5gaJpZM4NUeER .

josephst commented 7 years ago

Thanks for the feedback. I changed the extension so that it doesn't automatically update positions on pan/ zoom. The created Popper is still accessible via <cy|ele>.scratch("popper") so it's possible to access the Popper within an event listener (in code which uses the extension) if auto-updates are desired.

Just checked this myself and it works and it's undoubtedly a better way of handling updates if desired. I update the documentation for the extension to make it clear that this is the recommended way of updating position. Also added a screenshot of the extension and some examples/ API docs to move the extension closer to being ready for publication.

RosaRomeroGomez commented 7 years ago

I'm using Webpack and React (specifically create-react-app to set up my project) and I've tried with no success what @maxkfranz suggested months ago because I was getting the exact same error:

import React, { Component } from 'react';
import cytoscape from 'cytoscape';
import cyqtip from 'cytoscape-qtip';
cytoscape.use( cyqtip );

Then, in my componentDidMount() method I define my onClick method but when I fire the event I get again the same exact error :

componentDidMount() {
        conf.container = this.cyRef;
        conf.elements = this.props.values;
        const cy = cytoscape(conf);

        cy.on('click', 'node', function(evt){
            const node = evt.target;
                node.qtip({
                    content: 'hello'
                }, evt);

        });
        this.state = { cy };
    }

I've tried to downgrade jquery to the 2 version, but nothing, this is what my package.json looks like... I'm kind of stuck so any suggestions would be much appreciated :)

"dependencies": {
    "bootstrap": "^3.3.7",
    "cytoscape": "^3.2.6",
    "cytoscape-cola": "^2.0.1",
    "cytoscape-dagre": "^2.1.0",
    "cytoscape-qtip": "^2.7.1",
    "jquery.2": "^1.0.0",
    "react": "^16.1.1",
    "react-bootstrap": "^0.31.5",
    "react-bootstrap-sidebar": "0.0.1",
    "react-dom": "^16.1.1",
    "react-scripts": "1.0.17",
    "react-sidebar": "^2.3.2"
  },
maxkfranz commented 7 years ago

@RosaRomeroGomez I think you either need to set the qtip show event to tap or you need to use the qtip api to manually show().

RosaRomeroGomez commented 7 years ago

Thanks @maxkfranz for the suggestion. I ended up implementing the second option. Here it is how my componentDidMount() method looks now. At least now, I'm able to show the node id but now my problem is some styling issue. It displays the text in the top left corner of the screen.

 componentDidMount() {
        conf.container = this.cyRef;
        conf.elements = this.props.values;
        const cy = cytoscape(conf);

        this.state = { cy };

        cy.on('tap', 'node', function(event) {

            const node_data = event.target.data().id

               $('#cy').qtip({
                    content: {
                     text: event.target.data().id
                    },
                    show: {
                    event: 'click'
                    },
                    position: {
                        my: 'top center',
                        at: 'bottom center'
                     },
                    style: {
                     classes: 'qtip-bootstrap',
                      tip: {
                        width: 16,
                        height: 8
                      }
                    },
                });

         });

    }

This is the styles I have, not sure what's going on....

html {
  height: 100%;
  width: 100%;
  background-color: rgb(233, 233, 233);
  left: 0;
  top: 0;
  margin: 0;
  padding: 0;
}

body { 
  font: 14px helvetica neue, helvetica, arial, sans-serif;
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  margin: 0;
  padding: 0;
}

#cy {

        width: 100%;
        height: 100%;
        position: absolute;
        top: 0px;
        left: 0px;
}
maxkfranz commented 5 years ago

Closing, as cytoscape-popper has superseded this extension:

https://github.com/cytoscape/cytoscape.js-popper

nicky1038 commented 5 years ago

Error qtip.$domEle.qtip( opts ) is not a function may occur if yarn installs two versions of jQuery simultaneously. Adding this to package.json helped me:

"resolutions": { 
  "jquery": "2.2.4" //or whatever version that satisfies all requirements
}