How to add a tooltip to a selection? - draftjs

When the users selects some text, I want to be able to show a tooltip, right below the selected text?
Any ideas how can I do that?

You could add a component that creates the tooltip, such as paper-tooltip, or create one, even with css only, depends on your usecase.
Here is a W3 example of a CSS tooltip

As far as I can tell, react-draft-wysiwyg does not support arbitrary plugins in the same way that draft-js-plugins does.
Searching on NPM, the only text selection related plugin I found is draft-js-delete-selection-plugin. You could use that as a starting point, as well as look at the documentation for SelectionState.

Without any idea of what you have so far it is hard to provide more info. I have created a JS fiddle that shows a simple tool tip with an event listener that gets the selected text by element id
https://jsfiddle.net/03Lu28qb/1/
$(document).ready(function () {
const textSelectionTooltipContainer = document.createElement("div");
textSelectionTooltipContainer.setAttribute(
"id",
"textSelectionTooltipContainer"
);
textSelectionTooltipContainer.innerHTML = `<p id="textSelected">Selected! </p>`;
const bodyElement = document.getElementsByTagName("BODY")[0];
bodyElement.addEventListener("mouseup", function (e) {
var textu = document.getSelection().toString();
if (!textu.length) {
textSelectionTooltipContainer.remove();
}
});
document
.getElementById("textToSelect")
.addEventListener("mouseup", function (e) {
let textu = document.getSelection().toString();
let matchu = /\r|\n/.exec(textu);
if (textu.length && !matchu) {
let range = document.getSelection().getRangeAt(0);
rect = range.getBoundingClientRect();
scrollPosition = $(window).scrollTop();
containerTop = scrollPosition + rect.top - 50 + "px";
containerLeft = rect.left + rect.width / 2 - 50 + "px";
textSelectionTooltipContainer.style.transform =
"translate3d(" + containerLeft + "," + containerTop + "," + "0px)";
bodyElement.appendChild(textSelectionTooltipContainer);
}
});
});

If you trying to do it in react try this.
If you trying to do it in js try this.

Related

Chartjs custom tooltip is not removing from UI

I have included chart.js custom tooltip using example in official docs.
Problem:
If someone have mouse on chart then this custom tooltip is displayed. And while the mouse is in chart, if someone uses tabs/enter to navigate to another page, this custom tooltip function is not called. As a consequence, tooltip is not removed from UI in rest of the interactions with app. I have inspected the core library and I think it was only detecting mouseout event which in this case, didn't call this custom tooltip hook.
Below attached is my code for tooltip configuration, and I am using react-chartjs-2 wrapper to consume chart.js
Chart.js version: 2.9.1
react-chartjs-2: 2.8.0
tooltips: {
enabled: false,
placement: 'left',
caretSize: 5,
cornerRadius: 6,
custom: function(tooltipModel) {
let tooltipEl = document.getElementById('custom-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'custom-tooltip';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
let position = this._chart.canvas.getBoundingClientRect();
let clickPointPosition = position.left + window.pageXOffset + tooltipModel.caretX;
// Display, position, and set styles for font
tooltipEl.style.opacity = 0.9;
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = (clickPointPosition < tooltipEl.offsetWidth ? clickPointPosition : clickPointPosition - tooltipEl.offsetWidth) + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily;
tooltipEl.style.fontSize = tooltipModel.bodyFontSize + 'px';
tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle;
tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
tooltipEl.style.pointerEvents = 'none';
}
},
Just for reference, This issue was resolved by adding animation: false and var tooltipModel = this.
Luckily, this issue was resolved by chartjs. Otherwise an alternate solution is to hide the tooltip in componentWillUnmount hook of your component or react-chartjs-2 wrapper component.
Here is link to actual github issue which includes fiddles as well for demonstration.
https://github.com/chartjs/Chart.js/issues/7493

ChartJS: Custom tooltip always displaying

Im using ChartJS to create a graph on my website.
Im trying to create a custom tooltip. According to the documentation, this should be easy:
var myPieChart = new Chart(ctx, {
type: 'pie',
data: data,
options: {
tooltips: {
custom: function(tooltip) {
// tooltip will be false if tooltip is not visible or should be hidden
if (!tooltip) {
return;
}
}
}
}
});
My problem is that the tooptip is never false and because of this my custom tooltip is always displayed.
Please see this JSFiddle (line 42 is never executed)
Question: Is it a bug that tooltip is never false, or am I missing something?
The custom tooltip option is used for when you want to create/style your own tooltip using HTLM/CSS outside of the scope of the canvas (and not use the built in tooltips at all).
In order to do this, you must define a place outside of your canvas to contain your tooltip (e.g. a div) and then use that container within your tooltips.custom function.
Here is an example where I used a custom tooltip to display the hovered pie chart section percentage in the middle of the chart. In this example I'm generating my tooltip inside a div with id "chartjs-tooltip". Notice how I interact with this div in my tooltips.custom function to position and change the value.
Also, the correct way to check if the tooltip should be hidden is to check it's opacity. The tooltip object will always exist, but when it should not be visible, the opacity is set to 0.
Chart.defaults.global.tooltips.custom = function(tooltip) {
// Tooltip Element
var tooltipEl = document.getElementById('chartjs-tooltip');
// Hide if no tooltip
if (tooltip.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set Text
if (tooltip.body) {
var total = 0;
// get the value of the datapoint
var value = this._data.datasets[tooltip.dataPoints[0].datasetIndex].data[tooltip.dataPoints[0].index].toLocaleString();
// calculate value of all datapoints
this._data.datasets[tooltip.dataPoints[0].datasetIndex].data.forEach(function(e) {
total += e;
});
// calculate percentage and set tooltip value
tooltipEl.innerHTML = '<h1>' + (value / total * 100) + '%</h1>';
}
// calculate position of tooltip
var centerX = (this._chartInstance.chartArea.left + this._chartInstance.chartArea.right) / 2;
var centerY = ((this._chartInstance.chartArea.top + this._chartInstance.chartArea.bottom) / 2);
// Display, position, and set styles for font
tooltipEl.style.opacity = 1;
tooltipEl.style.left = centerX + 'px';
tooltipEl.style.top = centerY + 'px';
tooltipEl.style.fontFamily = tooltip._fontFamily;
tooltipEl.style.fontSize = tooltip.fontSize;
tooltipEl.style.fontStyle = tooltip._fontStyle;
tooltipEl.style.padding = tooltip.yPadding + 'px ' + tooltip.xPadding + 'px';
};
Here is the full codepen example.
I hope that helps clear things up!

Can anybody help me to resolve ng-grid filtering in a multi row Grid

Here is the plunker: http://plnkr.co/edit/fGVVOOIwvf4GrEj3XtJ6?p=preview
I have created a multi row grid and I would like to filter the grid data for each column header.
If I put an input box outside the grid it is working fine. If I put an input box inside the column header filtering is not working
Please see the code and help me for this.
Use this filterBar plugin. (I cannot take credit for this, I do not remember where I found it.)
Plugin
var filterBarPlugin = {
init: function(scope, grid) {
filterBarPlugin.scope = scope;
filterBarPlugin.grid = grid;
$scope.$watch(function() {
var searchQuery = "";
angular.forEach(filterBarPlugin.scope.columns, function(col) {
if (col.visible && col.filterText) {
var filterText = (col.filterText.indexOf('*') == 0 ? col.filterText.replace('*', '') : "^" + col.filterText) + ";";
searchQuery += col.displayName + ": " + filterText;
}
});
return searchQuery;
}, function(searchQuery) {
filterBarPlugin.scope.$parent.filterText = searchQuery;
filterBarPlugin.grid.searchProvider.evalFilter();
});
},
scope: undefined,
grid: undefined,
};
Change your header cell input ng-model to: col.filterText
<input type="text" placeholder="MY NAME" ng-model="col.filterText" ng-change="activateFilter()"/>
Add plugin to gridOptions
...
plugins: [filterBarPlugin],
...
Updated Plunker: Plunker
I know this is old, but in case someone else ends up here...
Here is a google group thread discussing this: https://groups.google.com/forum/#!topic/angular/lhu5Fbs97G4
and within that discussion you can find the following plnkr which does what you are wanting (and which I think the above answer references):
http://plnkr.co/edit/c8mHmAXattallFRzXSaG?p=preview

Javascript horizontal slider not working

I'm using an old version of the Slidedeck Plugin (v.1.4.5) and I have a javascript problem with one of the skins I'm using. More precisely, the horizontal slides are stitched together and I can't figure out how to fix this. I want each slide to be independent, without any content from the next or previous slide to be seen on the active slide.
You can see in the screenshot from below that the middle slide has visible content from the previous and next slide, which obviously I don't want to be visible.
I should mention that I have very limited knowledge of javascript / jQuery, and I suspect it's a js problem because the CSS is the same for all skins - the only variable is the script used by each skin.
You can see the slider behavior on this website and below is the full script used for the slider skin. I would appreciate any help on this. To change the slides click on each slide arrow from left or right side, or you can use the directional keys on the keyboard.
(function($){
SlideDeckSkin['fullwidth-sexy'] = function(slidedeck){
var ns = 'fullwidth-sexy';
var elems = {};
elems.slidedeck = $(slidedeck);
elems.frame = elems.slidedeck.closest('.skin-' + ns);
// Disable spines as this skin is not meant to function with spines on
elems.slidedeck.slidedeck().setOption('hideSpines', true);
elems.frame.append('PreviousNext');
elems.slidedeck.append('<div class="' + ns + '-mask left"></div><div class="' + ns + '-mask right"></div>');
elems.frame.find('.sd-' + ns + '-nav').bind('click', function(event){
event.preventDefault();
var $this = $(this);
elems.slidedeck.slidedeck().options.pauseAutoPlay = true;
if($this.hasClass('prev')){
elems.slidedeck.slidedeck().prev();
} else {
elems.slidedeck.slidedeck().next();
}
});
};
$(document).ready(function(){
$('.skin-fullwidth-sexy .slidedeck').each(function(){
if(typeof($.data(this, 'skin-fullwidth-sexy')) == 'undefined' || $.data(this, 'skin-fullwidth-sexy') == null){
$.data(this, 'skin-fullwidth-sexy', new SlideDeckSkin['fullwidth-sexy'](this));
}
});
});
})(jQuery);
The following might work, but it is hard to test without an example of what you are trying to do.
What I did is added a unique number to the ns variable (for namespace I assume.) This number is passed to the callback of the each function. (doc)
(function($){
SlideDeckSkin['fullwidth-sexy'] = function(slidedeck,uniqueName){
var ns = 'fullwidth-sexy'+uniqueName;
var elems = {};
elems.slidedeck = $(slidedeck);
elems.frame = elems.slidedeck.closest('.skin-' + ns);
// Disable spines as this skin is not meant to function with spines on
elems.slidedeck.slidedeck().setOption('hideSpines', true);
elems.frame.append('PreviousNext');
elems.slidedeck.append('<div class="' + ns + '-mask left"></div><div class="' + ns + '-mask right"></div>');
elems.frame.find('.sd-' + ns + '-nav').bind('click', function(event){
event.preventDefault();
var $this = $(this);
elems.slidedeck.slidedeck().options.pauseAutoPlay = true;
if($this.hasClass('prev')){
elems.slidedeck.slidedeck().prev();
} else {
elems.slidedeck.slidedeck().next();
}
});
};
$(document).ready(function(){
$('.skin-fullwidth-sexy .slidedeck').each(function(n){
if(typeof($.data(this, 'skin-fullwidth-sexy')) == 'undefined' || $.data(this, 'skin-fullwidth-sexy') == null){
$.data(this, 'skin-fullwidth-sexy', new SlideDeckSkin['fullwidth-sexy'+n](this,n));
}
});
});
})(jQuery);

VEMap Pan triggers VEMap.onclick

I'm using the Virtual Earth (or Bing!...) SDK and need to attach an event when someone clicks the map. Unfortunately panning the map also triggers the onclick event. Does anyone know of a work around?
function GetMap(){
map = new VEMap('dvMap');
map.LoadMap(new VELatLong(35.576916524038616,-80.9410858154297), 11, 'h',false);
mapIsInit = true;
map.AttachEvent('onclick', MapClick);
}
function MapClick(e){
var clickPnt = map.PixelToLatLong(new VEPixel(e.mapX,e.mapY));
Message('Map X: ' + clickPnt.Longitude + '\nMap Y: ' + clickPnt.Latitude + '\nZoom: ' + e.zoomLevel);
}
I'm encountering the same problem. It is certainly not what I would expect, a click event in my terminology implies that the distance between mouseDown and mouseUp is below a certain threshold.
Here is some code that works in my experiments:
<script type="text/javascript">
var mouseDownLocation;
var mouseClickThreshold = 5;
function init()
{
var map = new VEMap('myMap');
map.LoadMap(new VELatLong(-27.15,153),8,'r' ,false);
map.AttachEvent("onmousedown", function(e) {
var x = e.mapX;
var y = e.mapY;
mouseDownLocation = new VEPixel(x, y);
});
map.AttachEvent("onmouseup", function(e) {
var x = e.mapX;
var y = e.mapY;
if(Math.abs(mouseDownLocation.x - x) +
Math.abs(mouseDownLocation.y - y) > mouseClickThreshold) {
return;
}
pixel = new VEPixel(x, y);
var LL = map.PixelToLatLong(pixel);
document.getElementById("myMapInfo").innerHTML =
"Pixel X: " + x + " | Pixel Y: " + y +
"<br /> LatLong: " + LL +
"<br/>" + e.eventName;
});
}
</script>
The message will be displayed only if the mouse didn't move too much between the down and up events, i.e. a normal click should trigger it, a drag shouldn't.
This is using the VE API in version 6.2 and expects two divs with IDs "myMap" and "myMapInfo". It's also code that's experimental and could be improved upon, but the general approach seems ok.
It all depends on what you're trying to do. You could check for which mouse button was pressed during the onclick event handler. For example: If it was the Left Mouse Button then do nothing, else if it's the Right Mouse Button then do your logic to display a message, or plot pushpin, etc.
To clarify, only Panning the Map using the Mouse will trigger the onclick event. If you use the little arrows on the navigation dashboard, it will not trigger the onclick event.
Thanks Peter, that is working great with 6.3 as well. I'm discovering Bing maps and I'm a bit lost in the event handlers, so that helped!