react-tiny-popover
Advanced tools
Comparing version 5.0.0 to 5.0.1
@@ -22,15 +22,12 @@ "use strict"; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var React = __importStar(require("react")); | ||
var util_1 = require("./util"); | ||
var react_1 = __importDefault(require("react")); | ||
var ArrowContainer_1 = require("./ArrowContainer"); | ||
exports.ArrowContainer = ArrowContainer_1.ArrowContainer; | ||
var PopoverPortal_1 = require("./PopoverPortal"); | ||
var util_1 = require("./util"); | ||
var DEFAULT_POSITION_ORDER = ['top', 'right', 'left', 'bottom']; | ||
var Popover = /** @class */ (function (_super) { | ||
@@ -40,12 +37,9 @@ __extends(Popover, _super); | ||
var _this = _super.call(this, props) || this; | ||
_this.target = React.createRef(); | ||
_this.targetRect = null; | ||
_this.targetPositionIntervalHandler = null; | ||
_this.popoverDiv = null; | ||
_this.positionOrder = null; | ||
_this.target = react_1.default.createRef(); | ||
_this.targetRect = undefined; | ||
_this.targetPositionIntervalHandler = undefined; | ||
_this.popoverDiv = undefined; | ||
_this.positionOrder = undefined; | ||
_this.willUnmount = false; | ||
_this.willMount = false; | ||
_this.onResize = function () { | ||
_this.renderPopover(); | ||
}; | ||
_this.onClick = function (e) { | ||
@@ -62,4 +56,7 @@ var _a = _this.props, onClickOutside = _a.onClickOutside, isOpen = _a.isOpen; | ||
}; | ||
_this.onResize = function () { | ||
_this.renderPopover(); | ||
}; | ||
_this.state = { | ||
popoverInfo: null, | ||
popoverInfo: undefined, | ||
isTransitioningToClosed: false, | ||
@@ -81,2 +78,3 @@ internalisOpen: false, | ||
} | ||
// eslint-disable-next-line no-restricted-syntax | ||
return null; | ||
@@ -91,2 +89,19 @@ }; | ||
}; | ||
Popover.prototype.componentDidUpdate = function (prevProps) { | ||
var prevIsOpen = prevProps.isOpen, prevAlign = prevProps.align, prevPosition = prevProps.position, prevTransitionDuration = prevProps.transitionDuration, prevPadding = prevProps.padding, prevWindowBorderPadding = prevProps.windowBorderPadding; | ||
var _a = this.props, isOpen = _a.isOpen, position = _a.position, transitionDuration = _a.transitionDuration, align = _a.align, contentDestination = _a.contentDestination, padding = _a.padding, windowBorderPadding = _a.windowBorderPadding; | ||
this.positionOrder = this.getPositionPriorityOrder(position); | ||
var hasNewDestination = prevProps.contentDestination !== contentDestination; | ||
if (prevIsOpen !== isOpen || | ||
prevAlign !== align || | ||
prevPosition !== position || | ||
prevPadding !== padding || | ||
prevWindowBorderPadding !== windowBorderPadding || | ||
hasNewDestination) { | ||
this.updatePopover(isOpen); | ||
} | ||
if (prevTransitionDuration !== transitionDuration) { | ||
this.popoverDiv.style.transition = "opacity " + transitionDuration + "s"; | ||
} | ||
}; | ||
Popover.prototype.componentWillUnmount = function () { | ||
@@ -100,28 +115,94 @@ this.willUnmount = true; | ||
}; | ||
Popover.prototype.componentDidUpdate = function (prevProps) { | ||
var prevIsOpen = prevProps.isOpen, prevAlign = prevProps.align, prevPosition = prevProps.position, prevTransitionDuration = prevProps.transitionDuration; | ||
var _a = this.props, isOpen = _a.isOpen, position = _a.position, transitionDuration = _a.transitionDuration, align = _a.align; | ||
this.positionOrder = this.getPositionPriorityOrder(this.props.position); | ||
var hasNewDestination = prevProps.contentDestination !== this.props.contentDestination; | ||
if (prevIsOpen !== isOpen || prevAlign !== align || prevPosition !== position || hasNewDestination) { | ||
this.updatePopover(isOpen); | ||
Popover.prototype.getNudgedPopoverPosition = function (_a) { | ||
var top = _a.top, left = _a.left, width = _a.width, height = _a.height; | ||
var padding = this.props.windowBorderPadding; | ||
top = top < padding ? padding : top; | ||
top = top + height > window.innerHeight - padding ? window.innerHeight - padding - height : top; | ||
left = left < padding ? padding : left; | ||
left = left + width > window.innerWidth - padding ? window.innerWidth - padding - width : left; | ||
return { top: top, left: left }; | ||
}; | ||
Popover.prototype.getPositionPriorityOrder = function (position) { | ||
if (position && typeof position !== 'string') { | ||
if (util_1.Constants.DEFAULT_POSITIONS.every(function (defaultPosition) { return position.find(function (p) { return p === defaultPosition; }) !== undefined; })) { | ||
return util_1.arrayUnique(position); | ||
} | ||
var remainingPositions = util_1.Constants.DEFAULT_POSITIONS.filter(function (defaultPosition) { return position.find(function (p) { return p === defaultPosition; }) === undefined; }); | ||
return util_1.arrayUnique(__spreadArrays(position, remainingPositions)); | ||
} | ||
if (prevTransitionDuration !== transitionDuration) { | ||
this.popoverDiv.style.transition = "opacity " + transitionDuration + "s"; | ||
if (position && typeof position === 'string') { | ||
var remainingPositions = util_1.Constants.DEFAULT_POSITIONS.filter(function (defaultPosition) { return defaultPosition !== position; }); | ||
return util_1.arrayUnique(__spreadArrays([position], remainingPositions)); | ||
} | ||
return DEFAULT_POSITION_ORDER; | ||
}; | ||
Popover.prototype.render = function () { | ||
var _a = this.props, content = _a.content, children = _a.children; | ||
var _b = this.state, popoverInfo = _b.popoverInfo, isTransitioningToClosed = _b.isTransitioningToClosed; | ||
var popoverContent = null; | ||
if ((this.props.isOpen || isTransitioningToClosed) && this.popoverDiv && popoverInfo) { | ||
var getContent = function (args) { return (typeof content === 'function' ? content(args) : content); }; | ||
popoverContent = (React.createElement(PopoverPortal_1.PopoverPortal, { element: this.popoverDiv, container: this.props.contentDestination || window.document.body, children: getContent(popoverInfo) })); | ||
Popover.prototype.getLocationForPosition = function (position, newTargetRect, popoverRect) { | ||
var _a = this.props, padding = _a.padding, align = _a.align; | ||
var targetMidX = newTargetRect.left + newTargetRect.width / 2; | ||
var targetMidY = newTargetRect.top + newTargetRect.height / 2; | ||
var top; | ||
var left; | ||
switch (position) { | ||
case 'top': | ||
top = newTargetRect.top - popoverRect.height - padding; | ||
left = targetMidX - popoverRect.width / 2; | ||
if (align === 'start') { | ||
left = newTargetRect.left; | ||
} | ||
if (align === 'end') { | ||
left = newTargetRect.right - popoverRect.width; | ||
} | ||
break; | ||
case 'left': | ||
top = targetMidY - popoverRect.height / 2; | ||
left = newTargetRect.left - padding - popoverRect.width; | ||
if (align === 'start') { | ||
top = newTargetRect.top; | ||
} | ||
if (align === 'end') { | ||
top = newTargetRect.bottom - popoverRect.height; | ||
} | ||
break; | ||
case 'bottom': | ||
top = newTargetRect.bottom + padding; | ||
left = targetMidX - popoverRect.width / 2; | ||
if (align === 'start') { | ||
left = newTargetRect.left; | ||
} | ||
if (align === 'end') { | ||
left = newTargetRect.right - popoverRect.width; | ||
} | ||
break; | ||
case 'right': | ||
top = targetMidY - popoverRect.height / 2; | ||
left = newTargetRect.right + padding; | ||
if (align === 'start') { | ||
top = newTargetRect.top; | ||
} | ||
if (align === 'end') { | ||
top = newTargetRect.bottom - popoverRect.height; | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
return (React.createElement(React.Fragment, null, | ||
children(this.target), | ||
popoverContent)); | ||
return { top: top, left: left }; | ||
}; | ||
Popover.prototype.createContainer = function () { | ||
var _a = this.props, containerStyle = _a.containerStyle, containerClassName = _a.containerClassName; | ||
var container = window.document.createElement('div'); | ||
container.style.overflow = 'hidden'; | ||
if (containerStyle) { | ||
Object.keys(containerStyle).forEach(function (key) { | ||
return (container.style[key] = containerStyle[key]); | ||
}); | ||
} | ||
container.className = containerClassName; | ||
container.style.position = 'absolute'; | ||
container.style.top = '0'; | ||
container.style.left = '0'; | ||
return container; | ||
}; | ||
Popover.prototype.updatePopover = function (isOpen) { | ||
if (isOpen && this.target != null) { | ||
if (isOpen && this.target) { | ||
if (!this.popoverDiv || !this.popoverDiv.parentNode) { | ||
@@ -141,2 +222,37 @@ var transitionDuration = this.props.transitionDuration; | ||
}; | ||
Popover.prototype.startTargetPositionListener = function (checkInterval) { | ||
var _this = this; | ||
if (!this.targetPositionIntervalHandler) { | ||
this.targetPositionIntervalHandler = window.setInterval(function () { | ||
var newTargetRect = _this.target.current.getBoundingClientRect(); | ||
if (util_1.targetPositionHasChanged(_this.targetRect, newTargetRect)) { | ||
_this.renderPopover(); | ||
} | ||
_this.targetRect = newTargetRect; | ||
}, checkInterval); | ||
} | ||
}; | ||
Popover.prototype.removePopover = function () { | ||
var _this = this; | ||
// this should now be a callback to handle event listening upon portal disappearance | ||
var _a = this.props, transitionDuration = _a.transitionDuration, isOpen = _a.isOpen; | ||
if (this.popoverDiv) { | ||
this.popoverDiv.style.opacity = '0'; | ||
} | ||
var remove = function () { | ||
if (_this.willUnmount || !isOpen || !_this.popoverDiv.parentNode) { | ||
window.clearInterval(_this.targetPositionIntervalHandler); | ||
window.removeEventListener('resize', _this.onResize); | ||
window.removeEventListener('click', _this.onClick); | ||
_this.targetPositionIntervalHandler = undefined; | ||
_this.setState({ isTransitioningToClosed: false }); | ||
} | ||
}; | ||
if (!this.willUnmount) { | ||
this.removePopoverTimeout = window.setTimeout(remove, (transitionDuration || util_1.Constants.FADE_TRANSITION) * 1000); | ||
} | ||
else { | ||
remove(); | ||
} | ||
}; | ||
Popover.prototype.renderPopover = function (positionIndex) { | ||
@@ -148,5 +264,8 @@ var _this = this; | ||
} | ||
this.renderWithPosition({ position: this.positionOrder[positionIndex], targetRect: this.target.current.getBoundingClientRect() }, function (violation, rect) { | ||
this.renderWithPosition({ | ||
position: this.positionOrder[positionIndex], | ||
targetRect: this.target.current.getBoundingClientRect(), | ||
}, function (violation, rect) { | ||
var _a; | ||
var _b = _this.props, disableReposition = _b.disableReposition, contentLocation = _b.contentLocation; | ||
var _b = _this.props, disableReposition = _b.disableReposition, contentLocation = _b.contentLocation, contentDestination = _b.contentDestination; | ||
if (violation && !disableReposition && !(typeof contentLocation === 'object')) { | ||
@@ -160,7 +279,18 @@ _this.renderPopover(positionIndex + 1); | ||
var position = _this.positionOrder[positionIndex]; | ||
var _e = disableReposition ? { top: rectTop, left: rectLeft } : { top: nudgedTop, left: nudgedLeft }, top_1 = _e.top, left = _e.left; | ||
var _e = disableReposition | ||
? { top: rectTop, left: rectLeft } | ||
: { top: nudgedTop, left: nudgedLeft }, top_1 = _e.top, left = _e.left; | ||
if (contentLocation_1) { | ||
var targetRect = _this.target.current.getBoundingClientRect(); | ||
var popoverRect = _this.popoverDiv.getBoundingClientRect(); | ||
(_a = typeof contentLocation_1 === 'function' ? contentLocation_1({ targetRect: targetRect, popoverRect: popoverRect, position: position, align: align, nudgedLeft: nudgedLeft, nudgedTop: nudgedTop }) : contentLocation_1, top_1 = _a.top, left = _a.left); | ||
(_a = typeof contentLocation_1 === 'function' | ||
? contentLocation_1({ | ||
targetRect: targetRect, | ||
popoverRect: popoverRect, | ||
position: position, | ||
align: align, | ||
nudgedLeft: nudgedLeft, | ||
nudgedTop: nudgedTop, | ||
}) | ||
: contentLocation_1, top_1 = _a.top, left = _a.left); | ||
_this.popoverDiv.style.left = left.toFixed() + "px"; | ||
@@ -172,4 +302,4 @@ _this.popoverDiv.style.top = top_1.toFixed() + "px"; | ||
var destinationLeftOffset = 0; | ||
if (_this.props.contentDestination) { | ||
var destRect = _this.props.contentDestination.getBoundingClientRect(); | ||
if (contentDestination) { | ||
var destRect = contentDestination.getBoundingClientRect(); | ||
destinationTopOffset = -destRect.top; | ||
@@ -185,4 +315,4 @@ destinationLeftOffset = -destRect.left; | ||
} | ||
_this.popoverDiv.style.width = null; | ||
_this.popoverDiv.style.height = null; | ||
_this.popoverDiv.style.width = undefined; | ||
_this.popoverDiv.style.height = undefined; | ||
_this.renderWithPosition({ | ||
@@ -224,128 +354,20 @@ position: position, | ||
}; | ||
Popover.prototype.startTargetPositionListener = function (checkInterval) { | ||
var _this = this; | ||
if (this.targetPositionIntervalHandler === null) { | ||
this.targetPositionIntervalHandler = window.setInterval(function () { | ||
var newTargetRect = _this.target.current.getBoundingClientRect(); | ||
if (util_1.targetPositionHasChanged(_this.targetRect, newTargetRect)) { | ||
_this.renderPopover(); | ||
} | ||
_this.targetRect = newTargetRect; | ||
}, checkInterval); | ||
Popover.prototype.render = function () { | ||
var _a = this.props, content = _a.content, children = _a.children, isOpen = _a.isOpen, contentDestination = _a.contentDestination; | ||
var _b = this.state, popoverInfo = _b.popoverInfo, isTransitioningToClosed = _b.isTransitioningToClosed; | ||
var popoverContent; | ||
if ((isOpen || isTransitioningToClosed) && this.popoverDiv && popoverInfo) { | ||
var getContent = function (args) { | ||
return typeof content === 'function' ? content(args) : content; | ||
}; | ||
popoverContent = (react_1.default.createElement(PopoverPortal_1.PopoverPortal, { element: this.popoverDiv, container: contentDestination || window.document.body }, getContent(popoverInfo))); | ||
} | ||
return (react_1.default.createElement(react_1.default.Fragment, null, | ||
children(this.target), | ||
popoverContent)); | ||
}; | ||
Popover.prototype.getNudgedPopoverPosition = function (_a) { | ||
var top = _a.top, left = _a.left, width = _a.width, height = _a.height; | ||
var padding = this.props.windowBorderPadding; | ||
top = top < padding ? padding : top; | ||
top = top + height > window.innerHeight - padding ? window.innerHeight - padding - height : top; | ||
left = left < padding ? padding : left; | ||
left = left + width > window.innerWidth - padding ? window.innerWidth - padding - width : left; | ||
return { top: top, left: left }; | ||
}; | ||
Popover.prototype.removePopover = function () { | ||
var _this = this; | ||
// this should now be a callback to handle event listening upon portal disappearance | ||
var transitionDuration = this.props.transitionDuration; | ||
if (this.popoverDiv != null) { | ||
this.popoverDiv.style.opacity = '0'; | ||
} | ||
var remove = function () { | ||
if (_this.willUnmount || !_this.props.isOpen || !_this.popoverDiv.parentNode) { | ||
window.clearInterval(_this.targetPositionIntervalHandler); | ||
window.removeEventListener('resize', _this.onResize); | ||
window.removeEventListener('click', _this.onClick); | ||
_this.targetPositionIntervalHandler = null; | ||
_this.setState({ isTransitioningToClosed: false }); | ||
} | ||
}; | ||
if (!this.willUnmount) { | ||
this.removePopoverTimeout = window.setTimeout(remove, (transitionDuration || util_1.Constants.FADE_TRANSITION) * 1000); | ||
} | ||
else { | ||
remove(); | ||
} | ||
}; | ||
Popover.prototype.getPositionPriorityOrder = function (position) { | ||
if (position && typeof position !== 'string') { | ||
if (util_1.Constants.DEFAULT_POSITIONS.every(function (defaultPosition) { return position.find(function (p) { return p === defaultPosition; }) !== undefined; })) { | ||
return util_1.arrayUnique(position); | ||
} | ||
else { | ||
var remainingPositions = util_1.Constants.DEFAULT_POSITIONS.filter(function (defaultPosition) { return position.find(function (p) { return p === defaultPosition; }) === undefined; }); | ||
return util_1.arrayUnique(__spreadArrays(position, remainingPositions)); | ||
} | ||
} | ||
else if (position && typeof position === 'string') { | ||
var remainingPositions = util_1.Constants.DEFAULT_POSITIONS.filter(function (defaultPosition) { return defaultPosition !== position; }); | ||
return util_1.arrayUnique(__spreadArrays([position], remainingPositions)); | ||
} | ||
}; | ||
Popover.prototype.createContainer = function () { | ||
var _a = this.props, containerStyle = _a.containerStyle, containerClassName = _a.containerClassName; | ||
var container = window.document.createElement('div'); | ||
container.style.overflow = 'hidden'; | ||
if (containerStyle) { | ||
Object.keys(containerStyle).forEach(function (key) { return (container.style[key] = containerStyle[key]); }); | ||
} | ||
container.className = containerClassName; | ||
container.style.position = 'absolute'; | ||
container.style.top = '0'; | ||
container.style.left = '0'; | ||
return container; | ||
}; | ||
Popover.prototype.getLocationForPosition = function (position, newTargetRect, popoverRect) { | ||
var _a = this.props, padding = _a.padding, align = _a.align; | ||
var targetMidX = newTargetRect.left + newTargetRect.width / 2; | ||
var targetMidY = newTargetRect.top + newTargetRect.height / 2; | ||
var top; | ||
var left; | ||
switch (position) { | ||
case 'top': | ||
top = newTargetRect.top - popoverRect.height - padding; | ||
left = targetMidX - popoverRect.width / 2; | ||
if (align === 'start') { | ||
left = newTargetRect.left; | ||
} | ||
if (align === 'end') { | ||
left = newTargetRect.right - popoverRect.width; | ||
} | ||
break; | ||
case 'left': | ||
top = targetMidY - popoverRect.height / 2; | ||
left = newTargetRect.left - padding - popoverRect.width; | ||
if (align === 'start') { | ||
top = newTargetRect.top; | ||
} | ||
if (align === 'end') { | ||
top = newTargetRect.bottom - popoverRect.height; | ||
} | ||
break; | ||
case 'bottom': | ||
top = newTargetRect.bottom + padding; | ||
left = targetMidX - popoverRect.width / 2; | ||
if (align === 'start') { | ||
left = newTargetRect.left; | ||
} | ||
if (align === 'end') { | ||
left = newTargetRect.right - popoverRect.width; | ||
} | ||
break; | ||
case 'right': | ||
top = targetMidY - popoverRect.height / 2; | ||
left = newTargetRect.right + padding; | ||
if (align === 'start') { | ||
top = newTargetRect.top; | ||
} | ||
if (align === 'end') { | ||
top = newTargetRect.bottom - popoverRect.height; | ||
} | ||
break; | ||
} | ||
return { top: top, left: left }; | ||
}; | ||
Popover.defaultProps = { | ||
padding: util_1.Constants.DEFAULT_PADDING, | ||
windowBorderPadding: util_1.Constants.DEFAULT_WINDOW_PADDING, | ||
position: ['top', 'right', 'left', 'bottom'], | ||
position: DEFAULT_POSITION_ORDER, | ||
align: 'center', | ||
@@ -356,3 +378,3 @@ containerClassName: util_1.Constants.POPOVER_CONTAINER_CLASS_NAME, | ||
return Popover; | ||
}(React.Component)); | ||
}(react_1.default.Component)); | ||
exports.default = Popover; |
@@ -19,6 +19,8 @@ "use strict"; | ||
}; | ||
exports.arrayUnique = function (array) { return array.filter(function (value, index, self) { return self.indexOf(value) === index; }); }; | ||
exports.arrayUnique = function (array) { | ||
return array.filter(function (value, index, self) { return self.indexOf(value) === index; }); | ||
}; | ||
exports.rectsAreEqual = function (rectA, rectB) { | ||
return rectA === rectB || | ||
(rectA === null || rectA === void 0 ? void 0 : rectA.bottom) === (rectB === null || rectB === void 0 ? void 0 : rectB.bottom) && | ||
((rectA === null || rectA === void 0 ? void 0 : rectA.bottom) === (rectB === null || rectB === void 0 ? void 0 : rectB.bottom) && | ||
(rectA === null || rectA === void 0 ? void 0 : rectA.height) === (rectB === null || rectB === void 0 ? void 0 : rectB.height) && | ||
@@ -28,7 +30,7 @@ (rectA === null || rectA === void 0 ? void 0 : rectA.left) === (rectB === null || rectB === void 0 ? void 0 : rectB.left) && | ||
(rectA === null || rectA === void 0 ? void 0 : rectA.top) === (rectB === null || rectB === void 0 ? void 0 : rectB.top) && | ||
(rectA === null || rectA === void 0 ? void 0 : rectA.width) === (rectB === null || rectB === void 0 ? void 0 : rectB.width); | ||
(rectA === null || rectA === void 0 ? void 0 : rectA.width) === (rectB === null || rectB === void 0 ? void 0 : rectB.width)); | ||
}; | ||
exports.popoverInfosAreEqual = function (infoA, infoB) { | ||
return infoA === infoB || | ||
(infoA === null || infoA === void 0 ? void 0 : infoA.align) === (infoB === null || infoB === void 0 ? void 0 : infoB.align) && | ||
((infoA === null || infoA === void 0 ? void 0 : infoA.align) === (infoB === null || infoB === void 0 ? void 0 : infoB.align) && | ||
(infoA === null || infoA === void 0 ? void 0 : infoA.nudgedLeft) === (infoB === null || infoB === void 0 ? void 0 : infoB.nudgedLeft) && | ||
@@ -38,10 +40,10 @@ (infoA === null || infoA === void 0 ? void 0 : infoA.nudgedTop) === (infoB === null || infoB === void 0 ? void 0 : infoB.nudgedTop) && | ||
exports.rectsAreEqual(infoA === null || infoA === void 0 ? void 0 : infoA.targetRect, infoB === null || infoB === void 0 ? void 0 : infoB.targetRect) && | ||
(infoA === null || infoA === void 0 ? void 0 : infoA.position) === (infoB === null || infoB === void 0 ? void 0 : infoB.position); | ||
(infoA === null || infoA === void 0 ? void 0 : infoA.position) === (infoB === null || infoB === void 0 ? void 0 : infoB.position)); | ||
}; | ||
exports.targetPositionHasChanged = function (oldTargetRect, newTargetRect) { | ||
return oldTargetRect === null | ||
|| oldTargetRect.left !== newTargetRect.left | ||
|| oldTargetRect.top !== newTargetRect.top | ||
|| oldTargetRect.width !== newTargetRect.width | ||
|| oldTargetRect.height !== newTargetRect.height; | ||
return oldTargetRect === undefined || | ||
oldTargetRect.left !== newTargetRect.left || | ||
oldTargetRect.top !== newTargetRect.top || | ||
oldTargetRect.width !== newTargetRect.width || | ||
oldTargetRect.height !== newTargetRect.height; | ||
}; |
{ | ||
"name": "react-tiny-popover", | ||
"version": "5.0.0", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/alexkatz/react-tiny-popover.git" | ||
}, | ||
"main": "dist/Popover.js", | ||
"types": "dist/index.d.ts", | ||
"version": "5.0.1", | ||
"description": "A simple and highly customizable popover react higher order component with no other dependencies! Typescript friendly.", | ||
"keywords": [ | ||
@@ -18,10 +13,16 @@ "react", | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/alexkatz/react-tiny-popover.git" | ||
}, | ||
"license": "MIT", | ||
"author": "Alex Katz", | ||
"description": "A simple and highly customizable popover react higher order component with no other dependencies! Typescript friendly.", | ||
"main": "dist/Popover.js", | ||
"types": "dist/index.d.ts", | ||
"scripts": { | ||
"build": "tsc -p . && yarn copy-types", | ||
"watch": "tsc-watch -p . --onSuccess 'yarn copy-types'", | ||
"clean": "rm -rf dist/", | ||
"copy-types": "cp src/index.d.ts dist/index.d.ts", | ||
"start-demo": "cd docs && yarn start" | ||
"start-demo": "cd docs && yarn start", | ||
"watch": "tsc-watch -p . --onSuccess 'yarn copy-types'" | ||
}, | ||
@@ -31,7 +32,20 @@ "devDependencies": { | ||
"@types/react-dom": "^16.8.4", | ||
"@typescript-eslint/eslint-plugin": "^2.30.0", | ||
"@typescript-eslint/parser": "^2.30.0", | ||
"babel-eslint": "^10.1.0", | ||
"eslint": "^6.8.0", | ||
"eslint-config-airbnb-typescript": "^7.2.1", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-config-react-app": "^5.2.1", | ||
"eslint-plugin-flowtype": "^4.7.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-jsx-a11y": "^6.2.3", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"eslint-plugin-react": "^7.19.0", | ||
"eslint-plugin-react-hooks": "^4.0.0", | ||
"eslint-plugin-redux-saga": "^1.1.3", | ||
"prettier": "^2.0.5", | ||
"react": "^16.2.0", | ||
"react-dom": "^16.2.0", | ||
"tsc-watch": "^4.0.0", | ||
"tslint": "^5.18.0", | ||
"tslint-react": "^4.0.0", | ||
"typescript": "^3.7.2" | ||
@@ -38,0 +52,0 @@ }, |
114
README.md
@@ -37,16 +37,12 @@ # react-tiny-popover | ||
<Popover | ||
isOpen={isPopoverOpen} | ||
position={'top'} // preferred position | ||
content={( | ||
<div> | ||
Hi! I'm popover content. | ||
</div> | ||
)} | ||
isOpen={isPopoverOpen} | ||
position={'top'} // preferred position | ||
content={<div>Hi! I'm popover content.</div>} | ||
> | ||
{ref => ( | ||
{ref => ( | ||
<div ref={ref} onClick={() => setIsPopoverOpen(!isPopoverOpen)}> | ||
Click me! | ||
Click me! | ||
</div> | ||
)} | ||
</Popover> | ||
)} | ||
</Popover>; | ||
``` | ||
@@ -60,21 +56,19 @@ | ||
<Popover | ||
isOpen={isPopoverOpen} | ||
position={['top', 'right', 'left', 'bottom']} // if you'd like, supply an array of preferred positions ordered by priority | ||
padding={10} // adjust padding here! | ||
disableReposition // prevents automatic readjustment of content position that keeps your popover content within your window's bounds | ||
onClickOutside={() => setIsPopoverOpen(false)} // handle click events outside of the popover/target here! | ||
content={({ position, nudgedLeft, nudgedTop, targetRect, popoverRect }) => ( // you can also provide a render function that injects some useful stuff! | ||
<div> | ||
<div>Hi! I'm popover content. Here's my position: {position}.</div> | ||
<div>I'm {` ${nudgedLeft} `} pixels beyond the window horizontally!</div> | ||
<div>I'm {` ${nudgedTop} `} pixels beyond the window vertically!</div> | ||
</div> | ||
)} | ||
isOpen={isPopoverOpen} | ||
position={['top', 'right', 'left', 'bottom']} // if you'd like, supply an array of preferred positions ordered by priority | ||
padding={10} // adjust padding here! | ||
disableReposition // prevents automatic readjustment of content position that keeps your popover content within your window's bounds | ||
onClickOutside={() => setIsPopoverOpen(false)} // handle click events outside of the popover/target here! | ||
content={({ position, nudgedLeft, nudgedTop, targetRect, popoverRect }) => ( // you can also provide a render function that injects some useful stuff! | ||
<div> | ||
<div>Hi! I'm popover content. Here's my position: {position}.</div> | ||
<div>I'm {` ${nudgedLeft} `} pixels beyond the window horizontally!</div> | ||
<div>I'm {` ${nudgedTop} `} pixels beyond the window vertically!</div> | ||
</div> | ||
)} | ||
> | ||
{ref => ( | ||
<div onClick={() => setIsPopoverOpen(!isPopoverOpen)}> | ||
Click me! | ||
</div> | ||
)} | ||
</Popover> | ||
{ref => ( | ||
<div onClick={() => setIsPopoverOpen(!isPopoverOpen)}>Click me!</div> | ||
)} | ||
</Popover>; | ||
``` | ||
@@ -88,33 +82,33 @@ | ||
<Popover | ||
isOpen={isPopoverOpen} | ||
position={['top', 'right', 'left', 'bottom']} | ||
padding={10} | ||
onClickOutside={() => setIsPopoverOpen(false)} | ||
content={({ position, targetRect, popoverRect }) => ( | ||
<ArrowContainer // if you'd like an arrow, you can import the ArrowContainer! | ||
position={position} | ||
targetRect={targetRect} | ||
popoverRect={popoverRect} | ||
arrowColor={'blue'} | ||
arrowSize={10} | ||
arrowStyle={{ opacity: 0.7 }} | ||
> | ||
<div | ||
style={{ backgroundColor: 'blue', opacity: 0.7 }} | ||
onClick={() => setIsPopoverOpen(!isPopoverOpen)} | ||
> | ||
Hi! I'm popover content. Here's my position: {position}. | ||
</div> | ||
</ArrowContainer> | ||
)} | ||
isOpen={isPopoverOpen} | ||
position={['top', 'right', 'left', 'bottom']} | ||
padding={10} | ||
onClickOutside={() => setIsPopoverOpen(false)} | ||
content={({ position, targetRect, popoverRect }) => ( | ||
<ArrowContainer // if you'd like an arrow, you can import the ArrowContainer! | ||
position={position} | ||
targetRect={targetRect} | ||
popoverRect={popoverRect} | ||
arrowColor={'blue'} | ||
arrowSize={10} | ||
arrowStyle={{ opacity: 0.7 }} | ||
> | ||
<div | ||
style={{ backgroundColor: 'blue', opacity: 0.7 }} | ||
onClick={() => setIsPopoverOpen(!isPopoverOpen)} | ||
> | ||
Hi! I'm popover content. Here's my position: {position}. | ||
</div> | ||
</ArrowContainer> | ||
)} | ||
> | ||
{ref => ( | ||
{ref => ( | ||
<div ref={ref} onClick={() => setIsPopoverOpen(!isPopoverOpen)}> | ||
Click me! | ||
Click me! | ||
</div> | ||
)} | ||
</Popover> | ||
)} | ||
</Popover>; | ||
``` | ||
If you'd like to use a custom React element as `Popover`'s target, you'll have to pass the `ref` `Popover` provides to an inner DOM element of your component. The best way to accomplish this is with [React's ref forwarding API](https://reactjs.org/docs/forwarding-refs.html). Here's a simple Typescript example, as well: | ||
If you'd like to use a custom React element as `Popover`'s target, you'll have to pass the `ref` that `Popover` provides to an inner DOM element of your component. The best way to accomplish this is with [React's ref forwarding API](https://reactjs.org/docs/forwarding-refs.html). Here's a simple example, using Typescript: | ||
@@ -130,3 +124,3 @@ ```JSX | ||
const CustomComponent = React.forwardRef<HTMLDivElement, CustomComponentProps>((props, ref) => ( | ||
<div ref={ref} onClick={() => props.onClick()}> | ||
<div ref={ref} onClick={props.onClick}> | ||
{props.children} | ||
@@ -137,8 +131,8 @@ </div> | ||
const App: React.FC = () => { | ||
const [popoverIsOpen, setPopoverIsOpen] = useState(false); | ||
const [isPopoverOpen, setIsPopoverOpen] = useState(false); | ||
return ( | ||
<div> | ||
<Popover isOpen={popoverIsOpen} content={<div>hey from popover content</div>}> | ||
<Popover isOpen={isPopoverOpen} content={<div>hey from popover content</div>}> | ||
{ref => ( | ||
<CustomComponent ref={ref} onClick={() => setPopoverIsOpen(!popoverIsOpen)}> | ||
<CustomComponent ref={ref} onClick={() => setIsPopoverOpen(!isPopoverOpen)}> | ||
hey from a custom target component | ||
@@ -161,3 +155,3 @@ </CustomComponent> | ||
| ------------------- | -------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| children | `Function` | ✔️ | This function, of form `(ref: React.Ref) => JSX.Element`, will return the JSX.Element target that you'd like the popover content to track. In order to track that element, however, you must attach the provided `ref`. If you're using a custom React component, you'll have to employ React's ref forwarding. See the example above, or read more about that over at [React's ref forwarding docs](https://reactjs.org/docs/forwarding-refs.html).Sweet. | | ||
| children | `Function` | ✔️ | This function, of form `(ref: React.Ref) => JSX.Element`, will return the JSX.Element target that you'd like the popover content to track. In order to track that element, however, you must attach the provided `ref`. If you're using a custom React component, you'll have to employ React's ref forwarding. See the examples above, or read more about that over at [React's ref forwarding docs](https://reactjs.org/docs/forwarding-refs.html). Sweet. | | ||
| isOpen | `boolean` | ✔️ | When this boolean is set to true, the popover is visible and tracks the target. When the boolean is false, the popover content is neither visible nor present on the DOM. | | ||
@@ -164,0 +158,0 @@ | content | `JSX.Element` or `Function` | ✔️ | Here, you'll provide the content that will appear as the popover. Rather than a JSX element like a `<div>`, you may supply a function that returns a JSX.Element, which will look like this: `({ position, targetRect, popoverRect, align, nudgedLeft, nudgedTop }) => JSX.Element`. Here, `position` is of type `'top', 'bottom', 'left', 'right'`. `align` is of type `start`, `center`, or `end`. Both `targetRect` and `popoverRect` are `ClientRect` objects of format `{ height: number, width: number, top: number, left: number, right: number, bottom: number }`, and represent the popover content and target `div`'s coordinates within your browser's window. `nudgedLeft` and `nudgedTop` specify the X and Y offset the popover content is shifted by to keep it within the window's bounds during a boundary collision. You may want to use these values to adjust your content depending on its location in relation to the window and the target, especially if you have repositioning disabled. Sweet. | |
55985
10
606
21
177