Socket
Socket
Sign inDemoInstall

@codemirror/tooltip

Package Overview
Dependencies
Maintainers
2
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@codemirror/tooltip - npm Package Compare versions

Comparing version 0.18.4 to 0.19.0

12

CHANGELOG.md

@@ -0,1 +1,13 @@

## 0.19.0 (2021-08-11)
### Bug fixes
Move tooltips to avoid overlapping between them, when necessary.
Make sure tooltips don't stay visible when the editor goes out of view.
### New features
Hover tooltips are now grouped together in a single DOM element when multiple such tooltips are active.
## 0.18.4 (2021-03-15)

@@ -2,0 +14,0 @@

10

dist/index.d.ts

@@ -26,3 +26,5 @@ import { EditorView, ViewUpdate } from '@codemirror/view';

Whether the tooltip should be shown above or below the target
position. Defaults to false.
position. Not guaranteed for hover tooltips since all hover
tooltips for the same range are always positioned together.
Defaults to false.
*/

@@ -33,3 +35,3 @@ above?: boolean;

enough space on that side to show the tooltip inside the
viewport. Defaults to false.
viewport. Not guaranteed for hover tooltips. Defaults to false.
*/

@@ -71,2 +73,6 @@ strictSide?: boolean;

pointer is before the position, 1 if after the position.
Note that all hover tooltips are hosted within a single tooltip
container element. This allows multiple tooltips over the same
range to be "merged" together without overlapping.
*/

@@ -73,0 +79,0 @@ declare function hoverTooltip(source: (view: EditorView, pos: number, side: -1 | 1) => Tooltip | null | Promise<Tooltip | null>, options?: {

212

dist/index.js

@@ -5,57 +5,65 @@ import { ViewPlugin, Direction, EditorView, logException } from '@codemirror/view';

const ios = typeof navigator != "undefined" &&
!/Edge\/(\d+)/.exec(navigator.userAgent) && /Apple Computer/.test(navigator.vendor) &&
(/Mobile\/\w+/.test(navigator.userAgent) || navigator.maxTouchPoints > 2);
!/*@__PURE__*//Edge\/(\d+)/.exec(navigator.userAgent) && /*@__PURE__*//Apple Computer/.test(navigator.vendor) &&
(/*@__PURE__*//Mobile\/\w+/.test(navigator.userAgent) || navigator.maxTouchPoints > 2);
const Outside = "-10000px";
const tooltipPlugin = ViewPlugin.fromClass(class {
constructor(view) {
this.view = view;
this.inView = true;
this.measureReq = { read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this };
this.input = view.state.facet(showTooltip);
class TooltipViewManager {
constructor(view, facet, createTooltipView) {
this.facet = facet;
this.createTooltipView = createTooltipView;
this.input = view.state.facet(facet);
this.tooltips = this.input.filter(t => t);
this.tooltipViews = this.tooltips.map(tp => this.createTooltip(tp));
this.tooltipViews = this.tooltips.map(createTooltipView);
}
update(update) {
let input = update.state.facet(showTooltip);
if (input == this.input) {
const input = update.state.facet(this.facet);
const tooltips = input.filter(x => x);
if (input === this.input) {
for (let t of this.tooltipViews)
if (t.update)
t.update(update);
return { shouldMeasure: false };
}
else {
let tooltips = input.filter(x => x);
let views = [];
for (let i = 0; i < tooltips.length; i++) {
let tip = tooltips[i], known = -1;
if (!tip)
continue;
for (let i = 0; i < this.tooltips.length; i++) {
let other = this.tooltips[i];
if (other && other.create == tip.create)
known = i;
}
if (known < 0) {
views[i] = this.createTooltip(tip);
}
else {
let tooltipView = views[i] = this.tooltipViews[known];
if (tooltipView.update)
tooltipView.update(update);
}
let tooltipViews = [];
for (let i = 0; i < tooltips.length; i++) {
let tip = tooltips[i], known = -1;
if (!tip)
continue;
for (let i = 0; i < this.tooltips.length; i++) {
let other = this.tooltips[i];
if (other && other.create == tip.create)
known = i;
}
for (let t of this.tooltipViews)
if (views.indexOf(t) < 0)
t.dom.remove();
this.input = input;
this.tooltips = tooltips;
this.tooltipViews = views;
this.maybeMeasure();
if (known < 0) {
tooltipViews[i] = this.createTooltipView(tip);
}
else {
let tooltipView = tooltipViews[i] = this.tooltipViews[known];
if (tooltipView.update)
tooltipView.update(update);
}
}
for (let t of this.tooltipViews)
if (tooltipViews.indexOf(t) < 0)
t.dom.remove();
this.input = input;
this.tooltips = tooltips;
this.tooltipViews = tooltipViews;
return { shouldMeasure: true };
}
}
const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
constructor(view) {
this.view = view;
this.inView = true;
this.measureReq = { read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this };
this.manager = new TooltipViewManager(view, showTooltip, t => this.createTooltip(t));
}
update(update) {
const { shouldMeasure } = this.manager.update(update);
if (shouldMeasure)
this.maybeMeasure();
}
createTooltip(tooltip) {
let tooltipView = tooltip.create(this.view);
tooltipView.dom.classList.add("cm-tooltip");
// FIXME drop this on the next breaking release
if (tooltip.class)
tooltipView.dom.classList.add(tooltip.class);
tooltipView.dom.style.top = Outside;

@@ -68,3 +76,3 @@ this.view.dom.appendChild(tooltipView.dom);

destroy() {
for (let { dom } of this.tooltipViews)
for (let { dom } of this.manager.tooltipViews)
dom.remove();

@@ -75,4 +83,4 @@ }

editor: this.view.dom.getBoundingClientRect(),
pos: this.tooltips.map(t => this.view.coordsAtPos(t.pos)),
size: this.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
pos: this.manager.tooltips.map(t => this.view.coordsAtPos(t.pos)),
size: this.manager.tooltipViews.map(({ dom }) => dom.getBoundingClientRect()),
innerWidth: window.innerWidth,

@@ -84,4 +92,5 @@ innerHeight: window.innerHeight

let { editor } = measured;
for (let i = 0; i < this.tooltipViews.length; i++) {
let tooltip = this.tooltips[i], tView = this.tooltipViews[i], { dom } = tView;
let others = [];
for (let i = 0; i < this.manager.tooltips.length; i++) {
let tooltip = this.manager.tooltips[i], tView = this.manager.tooltipViews[i], { dom } = tView;
let pos = measured.pos[i], size = measured.size[i];

@@ -100,4 +109,8 @@ // Hide tooltips that are outside of the editor.

above = !above;
let top = above ? pos.top - height : pos.bottom, right = left + width;
for (let r of others)
if (r.left < right && r.right > left && r.top < top + height && r.bottom > top)
top = above ? r.top - height : r.bottom;
if (ios) {
dom.style.top = ((above ? pos.top - height : pos.bottom) - editor.top) + "px";
dom.style.top = (top - editor.top) + "px";
dom.style.left = (left - editor.left) + "px";

@@ -107,5 +120,6 @@ dom.style.position = "absolute";

else {
dom.style.top = (above ? pos.top - height : pos.bottom) + "px";
dom.style.top = top + "px";
dom.style.left = left + "px";
}
others.push({ left, top, right, bottom: top + height });
dom.classList.toggle("cm-tooltip-above", above);

@@ -118,6 +132,11 @@ dom.classList.toggle("cm-tooltip-below", !above);

maybeMeasure() {
if (this.tooltips.length) {
if (this.view.inView || this.inView)
if (this.manager.tooltips.length) {
if (this.view.inView)
this.view.requestMeasure(this.measureReq);
this.inView = this.view.inView;
if (this.inView != this.view.inView) {
this.inView = this.view.inView;
if (!this.inView)
for (let tv of this.manager.tooltipViews)
tv.dom.style.top = Outside;
}
}

@@ -130,3 +149,3 @@ }

});
const baseTheme = EditorView.baseTheme({
const baseTheme = /*@__PURE__*/EditorView.baseTheme({
".cm-tooltip": {

@@ -140,2 +159,5 @@ position: "fixed",

},
"&light .cm-tooltip-section:not(:first-child)": {
borderTop: "1px solid #ddd",
},
"&dark .cm-tooltip": {

@@ -146,18 +168,59 @@ backgroundColor: "#333338",

});
// FIXME backward-compat shim. Delete on next major version.
/**
@internal
*/
function tooltips() {
return [];
}
/**
Behavior by which an extension can provide a tooltip to be shown.
*/
const showTooltip = Facet.define({
const showTooltip = /*@__PURE__*/Facet.define({
enables: [tooltipPlugin, baseTheme]
});
const HoverTime = 750, HoverMaxDist = 6;
const showHoverTooltip = /*@__PURE__*/Facet.define();
class HoverTooltipHost {
constructor(view) {
this.view = view;
this.mounted = false;
this.dom = document.createElement("div");
this.dom.classList.add("cm-tooltip-hover");
this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t));
}
// Needs to be static so that host tooltip instances always match
static create(view) {
return new HoverTooltipHost(view);
}
createHostedView(tooltip) {
const hostedView = tooltip.create(this.view);
hostedView.dom.classList.add("cm-tooltip-section");
this.dom.appendChild(hostedView.dom);
if (this.mounted && hostedView.mount)
hostedView.mount(this.view);
return hostedView;
}
mount(view) {
for (const hostedView of this.manager.tooltipViews) {
if (hostedView.mount)
hostedView.mount(view);
}
this.mounted = true;
}
positioned() {
for (const hostedView of this.manager.tooltipViews) {
if (hostedView.positioned)
hostedView.positioned();
}
}
update(update) {
this.manager.update(update);
}
}
const showHoverTooltipHost = /*@__PURE__*/showTooltip.compute([showHoverTooltip], state => {
const tooltips = state.facet(showHoverTooltip).filter(t => t);
if (tooltips.length === 0)
return null;
return {
pos: Math.min(...tooltips.map(t => t.pos)),
end: Math.max(...tooltips.filter(t => t.end != null).map(t => t.end)),
create: HoverTooltipHost.create,
above: tooltips[0].above
};
});
class HoverPlugin {
constructor(view, source, field, setHover) {
constructor(view, source, field, setHover, hoverTime) {
this.view = view;

@@ -167,3 +230,5 @@ this.source = source;

this.setHover = setHover;
this.hoverTime = hoverTime;
this.lastMouseMove = null;
this.lastMoveTime = 0;
this.hoverTimeout = -1;

@@ -190,5 +255,5 @@ this.restartTimeout = -1;

return;
let now = Date.now(), lastMove = this.lastMouseMove;
if (now - lastMove.timeStamp < HoverTime)
this.hoverTimeout = setTimeout(this.checkHover, HoverTime - (now - lastMove.timeStamp));
let hovered = Date.now() - this.lastMoveTime;
if (hovered < this.hoverTime)
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime - hovered);
else

@@ -231,4 +296,5 @@ this.startHover();

this.lastMouseMove = event;
this.lastMoveTime = Date.now();
if (this.hoverTimeout < 0)
this.hoverTimeout = setTimeout(this.checkHover, HoverTime);
this.hoverTimeout = setTimeout(this.checkHover, this.hoverTime);
let tooltip = this.active;

@@ -238,3 +304,3 @@ if (tooltip && !isInTooltip(event.target) || this.pending) {

if ((pos == end ? this.view.posAtCoords({ x: event.clientX, y: event.clientY }) != pos
: !isOverRange(this.view, pos, end, event.clientX, event.clientY, HoverMaxDist))) {
: !isOverRange(this.view, pos, end, event.clientX, event.clientY, 6 /* MaxDist */))) {
this.view.dispatch({ effects: this.setHover.of(null) });

@@ -286,2 +352,6 @@ this.pending = null;

pointer is before the position, 1 if after the position.
Note that all hover tooltips are hosted within a single tooltip
container element. This allows multiple tooltips over the same
range to be "merged" together without overlapping.
*/

@@ -310,10 +380,12 @@ function hoverTooltip(source, options = {}) {

},
provide: f => showTooltip.from(f)
provide: f => showHoverTooltip.from(f)
});
let hoverTime = options.hoverTime || 750 /* Time */;
return [
hoverState,
ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover))
ViewPlugin.define(view => new HoverPlugin(view, source, hoverState, setHover, hoverTime)),
showHoverTooltipHost
];
}
export { hoverTooltip, showTooltip, tooltips };
export { hoverTooltip, showTooltip };
{
"name": "@codemirror/tooltip",
"version": "0.18.4",
"version": "0.19.0",
"description": "Tooltip support for the CodeMirror code editor",
"scripts": {
"test": "echo 'No tests'",
"test": "cm-runtests",
"prepare": "cm-buildhelper src/tooltip.ts"

@@ -29,7 +29,7 @@ },

"dependencies": {
"@codemirror/state": "^0.18.0",
"@codemirror/view": "^0.18.0"
"@codemirror/state": "^0.19.0",
"@codemirror/view": "^0.19.0"
},
"devDependencies": {
"@codemirror/buildhelper": "^0.1.0"
"@codemirror/buildhelper": "^0.1.5"
},

@@ -36,0 +36,0 @@ "repository": {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc