Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add new Custom Tooltip plugin #650

Merged
merged 14 commits into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 255 additions & 0 deletions examples/example-plugin-custom-tooltip.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
<!DOCTYPE HTML>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="shortcut icon" type="image/ico" href="favicon.ico" />
<title>SlickGrid example 3: Editing</title>
<link rel="stylesheet" href="../slick.grid.css" type="text/css" />
<link rel="stylesheet" href="../css/smoothness/jquery-ui.css" type="text/css" />
<link rel="stylesheet" href="../plugins/slick.customtooltip.css" type="text/css" />
<link rel="stylesheet" href="examples.css" type="text/css" />
<style>
.cell-title, .bold {
font-weight: bold;
}

.cell-effort-driven {
text-align: center;
}

.header-tooltip-title {
font-weight: bold;
font-size: 14px;
}
.tooltip-2cols-row {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
column-gap: 5px;
line-height: 20px;
}

/* change css of 3rd column (l2 r2) */
.l2.slick-custom-tooltip {
background-color: #363636;
color: #ffffff;
border: 2px solid #252525;
/* overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; */
}
.l2.slick-custom-tooltip.tooltip-arrow.arrow-up::before {
border-bottom: 7px solid #252525;
}
.l2.slick-custom-tooltip.tooltip-arrow.arrow-down::after {
border-top: 7px solid #252525;
}
</style>
</head>

<body>
<div style="position:relative">
<div style="width:618px;">
<div id="myGrid" style="width:100%;height:500px;"></div>
</div>

<div class="options-panel">
<h2>Demonstrates:</h2>
<ul>
<li>Custom Tooltip Plugin</li>
<li>1st column shows an async tooltip (could be an API with Promise), it demos a formatter which itself has a
formatter displaying percent complete bar
</li>
<li>2nd column is with "useRegularTooltip" which will parse your cell formatters in search of a "title" attribute to use as tooltip</li>
<li>Every other columns will show other info of the item data context</li>
<li>Tooltip gets auto-position depending on its available space</li>
<li>You can disable a tooltip via "usabilityOverride"</li>
</ul>

<h2>Options:</h2>
<button onclick="customTooltipPlugin.setOptions({hideArrow:true})">Hide Tooltip Arrow ON</button>
&nbsp;
<button onclick="customTooltipPlugin.setOptions({hideArrow:false})">Hide Tooltip Arrow OFF</button>
<br/>
<br/>
<button onclick="disableTooltip(true)">Disable Tooltip ON</button>
&nbsp;
<button onclick="disableTooltip(false)">Disable Tooltip OFF</button>
<h2>View Source:</h2>
<ul>
<li><A href="https://github.com/6pac/SlickGrid/blob/master/examples/example-plugin-custom-tooltip.html"
target="_sourcewindow"> View the source for this example on Github</a></li>
</ul>
</div>
</div>

<script src="../lib/firebugx.js"></script>

<script src="../lib/jquery-1.12.4.min.js"></script>
<script src="../lib/jquery-ui.min.js"></script>
<script src="../lib/jquery.event.drag-2.3.0.js"></script>

<script src="../slick.core.js"></script>
<script src="../plugins/slick.checkboxselectcolumn.js"></script>
<script src="../plugins/slick.cellrangedecorator.js"></script>
<script src="../plugins/slick.cellrangeselector.js"></script>
<script src="../plugins/slick.cellselectionmodel.js"></script>
<script src="../plugins/slick.rowselectionmodel.js"></script>
<script src="../plugins/slick.customtooltip.js"></script>
<script src="../slick.formatters.js"></script>
<script src="../slick.editors.js"></script>
<script src="../slick.grid.js"></script>

<script>
function requiredFieldValidator(value) {
if (value == null || value == undefined || !value.length) {
return { valid: false, msg: "This is a required field" };
} else {
return { valid: true, msg: null };
}
}

function disableTooltip(disable) {
if (disable) {
isTooltipDisabled = true;
// customTooltipPlugin.setOptions({ usabilityOverride: function (args) { return false;}});
} else {
isTooltipDisabled = false;
// customTooltipPlugin.setOptions({ usabilityOverride: function (args) { return args.cell !== 0;}});
}
}

var isTooltipDisabled = false;
var grid;
var data = [];
var customTooltipPlugin;
var columns = [
{
id: "title", name: "Title", field: "title", width: 70, cssClass: "cell-title",
editor: Slick.Editors.Text, validator: requiredFieldValidator,
customTooltip: {
// position: 'bottom', // defaults to "auto", available options are: "auto", "top", "left", "bottom", "right"
// you can use the Custom Tooltip in 2 ways (synchronous or asynchronous)
// example 1 (sync):
// formatter: this.tooltipTaskFormatter.bind(this),

// example 2 (async):
// when using async, the `formatter` will contain the loading spinner
// you will need to provide an `asyncPost` function returning a Promise and also `asyncPostFormatter` formatter to display the result once the Promise resolves
formatter: () => `<div>loading...</div>`,
asyncProcess: () => new Promise(resolve => {
setTimeout(() => resolve({ ratio: Math.random() * 10 / 10, lifespan: Math.random() * 100 }), 500);
}),
asyncPostFormatter: this.tooltipTaskFormatter.bind(this),

// optional conditional usability callback
// usabilityOverride: (args) => !!(args.dataContext?.id % 2) // show it only every second row
},
},
{
id: "desc", name: '<span title="custom title tooltip text">Description</span>', field: "description", width: 100, editor: Slick.Editors.LongText,
formatter: (row, cell, value, column, dataContext) => `<span title="<b>regular tooltip</b> (from title attribute) \r${dataContext.title} cell value:\r ${value || ''}">${value || ''}</span>`,
// define tooltip options here OR for the entire grid via the grid options (cell tooltip options will have precedence over grid options)
customTooltip: {
useRegularTooltip: true, // note regular tooltip will try to find a "title" attribute in the cell formatter (it won't work without a cell formatter)
renderRegularTooltipAsHtml: true, // defaults to false, regular "title" tooltip won't be rendered as html unless specified via this flag (also "\r\n" will be replaced by <br>)
// maxWidth: 100,
// maxHeight: 30,
},
},
{ id: "duration", name: "Duration", field: "duration", editor: Slick.Editors.Text },
{ id: "%", name: "% Complete", field: "percentComplete", width: 80, resizable: false, formatter: Slick.Formatters.PercentCompleteBar, editor: Slick.Editors.PercentComplete },
{ id: "start", name: "Start", field: "start", minWidth: 60, editor: Slick.Editors.Date },
{ id: "finish", name: "Finish", field: "finish", minWidth: 60, editor: Slick.Editors.Date },
{ id: "effortDriven", name: "Effort Driven", width: 80, minWidth: 20, maxWidth: 80, cssClass: "cell-effort-driven", field: "effortDriven", formatter: Slick.Formatters.Checkmark, editor: Slick.Editors.Checkbox }
];
var gridOptions = {
editable: true,
enableAddRow: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: false,
multiSelect: true,
// Custom Tooltip options can be defined in a Column or Grid Options or a mixed of both (first options found wins)
customTooltip: {
formatter: tooltipFormatter,
headerFormatter: headerFormatter,
usabilityOverride: function (args) {
return !isTooltipDisabled && args.cell !== 0; // don't show on first column (row selection column)
}
// hideArrow: true, // defaults to False
},
};

function headerFormatter(row, cell, value, column, dataContext) {
const tooltipTitle = 'Custom Tooltip - Header';
return '<div class="header-tooltip-title" style="font-weight: bold">' + tooltipTitle + '</div>'
+ '<div class="tooltip-2cols-row"><div>Column:</div> <div>' + column.name + '</div></div>';
}

function tooltipFormatter(row, cell, value, column, dataContext) {
const tooltipTitle = 'Custom Tooltip';
const effortDrivenHtml = Slick.Formatters.Checkmark(row, cell, dataContext.effortDriven, column, dataContext);
const completionBarHtml = Slick.Formatters.PercentCompleteBar(row, cell, dataContext.percentComplete, column, dataContext);
return '<div class="bold">' + tooltipTitle + '</div>'
+ '<div class="tooltip-2cols-row"><div>Id:</div> <div>' + dataContext.id + '</div></div>'
+ '<div class="tooltip-2cols-row"><div>Title:</div> <div>' + dataContext.title + '</div></div>'
+ '<div class="tooltip-2cols-row"><div>Completion:</div> <div>' + completionBarHtml + '</div></div>'
+ '<div class="tooltip-2cols-row"><div>Effort Driven:</div> <div>' + effortDrivenHtml + '</div></div>';
}

function tooltipTaskFormatter(row, cell, value, column, dataContext, grid) {
const tooltipTitle = 'Task ' + dataContext.id + ' - (async tooltip)';

// use a 2nd Formatter to get the percent completion
// any properies provided from the `asyncPost` will end up in the `__params` property (unless a different prop name is provided via `asyncParamsPropName`)
const completionBar = Slick.Formatters.PercentCompleteBar(row, cell, dataContext.percentComplete, column, dataContext);
const out = '<div class="bold">' + tooltipTitle + '</div>'
+ '<div class="tooltip-2cols-row"><div>Completion:</div> <div>' + completionBar + '</div></div>'
+ '<div class="tooltip-2cols-row"><div>Lifespan:</div> <div>' + dataContext.__params.lifespan.toFixed(2) + '</div></div>'
+ '<div class="tooltip-2cols-row"><div>Ratio:</div> <div>'+dataContext.__params.ratio.toFixed(2) + '</div></div>';
return out;
}

$(function () {
for (var i = 0; i < 500; i++) {
var d = (data[i] = {});

d["id"] = i;
d["title"] = "Task " + i;
d["description"] = "This is a sample task description.\n It can be multiline";
d["duration"] = "5 days";
d["percentComplete"] = Math.round(Math.random() * 100);
d["start"] = "01/01/2009";
d["finish"] = "01/05/2009";
d["effortDriven"] = (i % 5 == 0);
}

var checkboxSelector = new Slick.CheckboxSelectColumn({
cssClass: "slick-cell-checkboxsel"
});
columns.unshift(checkboxSelector.getColumnDefinition());
grid = new Slick.Grid("#myGrid", data, columns, gridOptions);
grid.setSelectionModel(new Slick.RowSelectionModel({ selectActiveRow: false }));
grid.registerPlugin(checkboxSelector);

customTooltipPlugin = new Slick.Plugins.CustomTooltip();
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.registerPlugin(customTooltipPlugin);

grid.onAddNewRow.subscribe(function (e, args) {
var item = args.item;
grid.invalidateRow(data.length);
data.push(item);
grid.updateRowCount();
grid.render();
});
})
</script>
</body>

</html>
46 changes: 46 additions & 0 deletions plugins/slick.customTooltip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.slick-custom-tooltip {
position: absolute;
background-color: #ffffff;
border: 2px solid #acacac;
border-radius: 4px;
color: inherit;
padding: 7px;
height: auto;
width: auto;
z-index: 200;
overflow: initial;
text-overflow: ellipsis;
white-space: normal;
}
.slick-custom-tooltip::after {
content: "";
clear: both;
display: table;
}

/* show arrow below tooltip with same color as the border color */
.slick-custom-tooltip::after,
.slick-custom-tooltip::before {
content: "";
height: 0;
position: absolute;
width: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
}
.slick-custom-tooltip.tooltip-arrow.arrow-up::before {
border-bottom: 7px solid #acacac;
top: -7px;
}
.slick-custom-tooltip.tooltip-arrow.arrow-down::after {
border-top: 7px solid #acacac;
bottom: -7px;
}
.slick-custom-tooltip.tooltip-arrow.arrow-left::after
.slick-custom-tooltip.tooltip-arrow.arrow-left::before {
margin-left: -10%;
}
.slick-custom-tooltip.tooltip-arrow.arrow-right::after,
.slick-custom-tooltip.tooltip-arrow.arrow-right::before {
margin-left: 70%;
}
Loading