@enact/spotlight
Advanced tools
Comparing version 2.2.0-alpha.1 to 2.2.0
@@ -5,2 +5,19 @@ # Change Log | ||
## [2.2.0] - 2018-10-02 | ||
### Changed | ||
- `spotlight` to not explicitly `blur()` the currently focused element when focusing another, allowing the platform to manage blurring before focus | ||
### Fixed | ||
- `spotlight` to correctly set focus when the window is activated | ||
- `spotlight` to correctly set focus when entering a restricted container | ||
## [2.1.4] - 2018-09-17 | ||
### Fixed | ||
- `spotlight/Spottable` to respect paused state when it becomes enabled | ||
## [2.1.3] - 2018-09-10 | ||
@@ -7,0 +24,0 @@ |
{ | ||
"name": "@enact/spotlight", | ||
"version": "2.2.0-alpha.1", | ||
"version": "2.2.0", | ||
"description": "A focus management library", | ||
@@ -24,3 +24,3 @@ "main": "src/spotlight.js", | ||
"dependencies": { | ||
"@enact/core": "^2.2.0-alpha.1", | ||
"@enact/core": "^2.2.0", | ||
"prop-types": "^15.6.0", | ||
@@ -27,0 +27,0 @@ "ramda": "^0.24.1", |
@@ -18,5 +18,19 @@ "use strict"; | ||
/** | ||
* Provides the {@link spotlight/Pause.Pause} class which allows consumers to pause spotlight and | ||
* then only resume spotlight if another caller had not also paused Spotlight. | ||
* Provides a class which allows consumers to safely pause and resume spotlight without resuming | ||
* another consumer's pause. | ||
* | ||
* When multiple components attempt to pause and resume spotlight at overlapping times using | ||
* [Spotlight.pause()]{@link spotlight.Spotlight.pause} and | ||
* [Spotlight.resume()]{@link spotlight.Spotlight.resume}, one component might resume spotlight when | ||
* another expected it to still be paused. | ||
* | ||
* `Pause` helps to address this by setting a "soft lock" on the pause which informs other instances | ||
* that the spotlight pause state is being controlled. When pause is locked, it can only be resumed | ||
* by the instance that locked it. Subsequent calls to `pause` and `resume` on another instance of | ||
* `Pause` have no effect. | ||
* | ||
* *Note:* The top-level [Spotlight.pause()]{@link spotlight.Spotlight.pause} and | ||
* [Spotlight.resume()]{@link spotlight.Spotlight.resume} do not respect the pause locks and act as | ||
* a user-space escape hatch. | ||
* | ||
* ``` | ||
@@ -31,11 +45,11 @@ * import Pause from '@enact/spotlight/Pause'; | ||
* | ||
* // spotlight is still paused and controlling Pause is | ||
* // updated to paused2 | ||
* // has no effect because pause1 is in control | ||
* paused2.pause(); | ||
* | ||
* // has no effect because paused2 is in control | ||
* // has no effect because pause1 is in control | ||
* paused2.resume(); | ||
* | ||
* // resumes spotlight | ||
* paused1.resume(); | ||
* | ||
* // resumes spotlight | ||
* paused2.resume(); | ||
* ``` | ||
@@ -42,0 +56,0 @@ * |
@@ -159,3 +159,3 @@ "use strict"; | ||
if (isSpottable(this.props) && !isSpottable(prevProps)) { | ||
if (isSpottable(this.props) && !isSpottable(prevProps) && !_spotlight.default.isPaused()) { | ||
if (_spotlight.default.getPointerMode()) { | ||
@@ -169,3 +169,3 @@ if (this.isHovered) { | ||
} | ||
} else if (!_spotlight.default.getCurrent() && !_spotlight.default.isPaused()) { | ||
} else if (!_spotlight.default.getCurrent()) { | ||
var containers = (0, _container.getContainersForNode)(this.node); | ||
@@ -172,0 +172,0 @@ |
@@ -18,2 +18,5 @@ "use strict"; | ||
exports.mayActivateContainer = mayActivateContainer; | ||
exports.notifyLeaveContainer = notifyLeaveContainer; | ||
exports.notifyLeaveContainerFail = notifyLeaveContainerFail; | ||
exports.notifyEnterContainer = notifyEnterContainer; | ||
exports.setContainerPreviousTarget = setContainerPreviousTarget; | ||
@@ -97,2 +100,9 @@ exports.setDefaultContainer = setDefaultContainer; | ||
navigableFilter: null, | ||
obliqueMultiplier: 5, | ||
onEnterContainer: null, | ||
// @private - notify the container when entering via 5-way | ||
onLeaveContainer: null, | ||
// @private - notify the container when leaving via 5-way | ||
onLeaveContainerFail: null, | ||
// @private - notify the container when failing to leave via 5-way | ||
overflow: false, | ||
@@ -105,2 +115,3 @@ rememberSource: false, | ||
selectorDisabled: false, | ||
straightMultiplier: 1, | ||
straightOnly: false, | ||
@@ -787,4 +798,5 @@ straightOverlapThreshold: 0.5, | ||
var config = getContainerConfig(containerId); | ||
var enterLast = config.enterTo === 'last-focused'; | ||
var enterDefault = config.enterTo === 'default-element'; | ||
var enterTo = config.enterTo, | ||
overflow = config.overflow; | ||
var enterLast = enterTo === 'last-focused'; | ||
var next; // if the container has a preferred entry point, try to find it first | ||
@@ -794,11 +806,26 @@ | ||
next = getContainerLastFocusedElement(containerId); | ||
} else if (enterDefault) { | ||
next = getContainerDefaultElement(containerId); | ||
} // if there isn't a preferred entry or it wasn't found, try to find something to spot | ||
} // try default element if last focused can't be focused | ||
if (!next) { | ||
next = !enterDefault && getContainerDefaultElement(containerId) || getSpottableDescendants(containerId); | ||
next = getContainerDefaultElement(containerId); | ||
} | ||
if (!next) { | ||
var spottables = getSpottableDescendants(containerId); // if there isn't a preferred entry on an overflow container, try to find the first element | ||
// in view | ||
if (overflow) { | ||
var containerRect = (0, _utils.getContainerRect)(containerId); | ||
next = spottables.find(function (element) { | ||
return (0, _utils.contains)(containerRect, (0, _utils.getRect)(element)); | ||
}); | ||
} // otherwise, return all spottables within the container | ||
if (!next) { | ||
next = spottables; | ||
} | ||
} | ||
return next ? (0, _util.coerceArray)(next) : []; | ||
@@ -1036,2 +1063,81 @@ } | ||
}); | ||
} | ||
/** | ||
* Notifies any affected containers that focus has left one of their children for another container | ||
* | ||
* @param {String} direction up/down/left/right | ||
* @param {Node} current currently focused element | ||
* @param {String[]} currentContainerIds Containers for current | ||
* @param {Node} next To be focused element | ||
* @param {String[]} nextContainerIds Containers for next | ||
* @private | ||
*/ | ||
function notifyLeaveContainer(direction, current, currentContainerIds, next, nextContainerIds) { | ||
currentContainerIds.forEach(function (containerId) { | ||
if (!nextContainerIds.includes(containerId)) { | ||
var config = getContainerConfig(containerId); | ||
if (config && config.onLeaveContainer) { | ||
config.onLeaveContainer({ | ||
type: 'onLeaveContainer', | ||
direction: direction, | ||
target: current, | ||
relatedTarget: next | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* Notifies any containers that focus attempted to move but failed to find a target | ||
* | ||
* @param {String} direction up/down/left/right | ||
* @param {Node} current currently focused element | ||
* @param {String[]} currentContainerIds Containers for current | ||
* @private | ||
*/ | ||
function notifyLeaveContainerFail(direction, current, currentContainerIds) { | ||
currentContainerIds.forEach(function (containerId) { | ||
var config = getContainerConfig(containerId); | ||
if (config && config.onLeaveContainerFail) { | ||
config.onLeaveContainerFail({ | ||
type: 'onLeaveContainerFail', | ||
direction: direction, | ||
target: current | ||
}); | ||
} | ||
}); | ||
} | ||
/** | ||
* Notifies any affected containers that one of their children has received focus. | ||
* | ||
* @param {String} direction up/down/left/right | ||
* @param {Node} previous Previously focused element | ||
* @param {String[]} previousContainerIds Containers for previous | ||
* @param {Node} current Currently focused element | ||
* @param {String[]} currentContainerIds Containers for current | ||
* @private | ||
*/ | ||
function notifyEnterContainer(direction, previous, previousContainerIds, current, currentContainerIds) { | ||
currentContainerIds.forEach(function (containerId) { | ||
if (!previousContainerIds.includes(containerId)) { | ||
var config = getContainerConfig(containerId); | ||
if (config && config.onEnterContainer) { | ||
config.onEnterContainer({ | ||
type: 'onEnterContainer', | ||
direction: direction, | ||
target: current, | ||
relatedTarget: previous | ||
}); | ||
} | ||
} | ||
}); | ||
} |
@@ -9,5 +9,3 @@ "use strict"; | ||
var obliqueMinDistance = 1; | ||
var obliqueMultiplier = 5; | ||
var straightMinDistance = 0; | ||
var straightMultiplier = 1; | ||
@@ -273,6 +271,10 @@ var calcGroupId = function calcGroupId(_ref) { | ||
var distanceFunction = generateDistancefunction(targetRect); | ||
var groups = partition(rects, targetRect, config.straightOverlapThreshold, function (rect, destRect) { | ||
var obliqueMultiplier = config.obliqueMultiplier, | ||
straightMultiplier = config.straightMultiplier, | ||
straightOnly = config.straightOnly, | ||
straightOverlapThreshold = config.straightOverlapThreshold; | ||
var groups = partition(rects, targetRect, straightOverlapThreshold, function (rect, destRect) { | ||
return calcGroupId(direction === 'up' || direction === 'down' ? calcNextExtendedGridPosition(rect, destRect) : calcNextGridPosition(rect, destRect)); | ||
}); | ||
var internalGroups = partition(groups[4], targetRect.center, config.straightOverlapThreshold, function (rect, destRect) { | ||
var internalGroups = partition(groups[4], targetRect.center, straightOverlapThreshold, function (rect, destRect) { | ||
return calcGroupId(calcNextGridPosition(rect, destRect)); | ||
@@ -403,3 +405,3 @@ }); | ||
if (config.straightOnly) { | ||
if (straightOnly) { | ||
priorities.splice(2, 2); | ||
@@ -406,0 +408,0 @@ } |
@@ -178,6 +178,2 @@ "use strict"; | ||
var silentFocus = function silentFocus() { | ||
if (currentFocusedElement) { | ||
currentFocusedElement.blur(); | ||
} | ||
elem.focus(focusOptions); | ||
@@ -200,6 +196,2 @@ focusChanged(elem, containerIds); | ||
if (currentFocusedElement) { | ||
currentFocusedElement.blur(); | ||
} | ||
elem.focus(focusOptions); | ||
@@ -299,6 +291,10 @@ _duringFocusChange = false; | ||
(0, _container.notifyLeaveContainer)(direction, currentFocusedElement, currentContainerIds, next, nextContainerIds); | ||
(0, _container.setContainerPreviousTarget)(currentContainerId, direction, next, currentFocusedElement); | ||
return focusElement(next, nextContainerIds); | ||
var focused = focusElement(next, nextContainerIds); | ||
(0, _container.notifyEnterContainer)(direction, currentFocusedElement, currentContainerIds, next, nextContainerIds); | ||
return focused; | ||
} | ||
(0, _container.notifyLeaveContainerFail)(direction, currentFocusedElement, currentContainerIds); | ||
return false; | ||
@@ -352,3 +348,11 @@ } | ||
if (!Spotlight.focus((0, _container.getContainerLastFocusedElement)(_container.rootContainerId))) { | ||
var lastFocusedElement = (0, _container.getContainerLastFocusedElement)(_container.rootContainerId); | ||
while ((0, _container.isContainer)(lastFocusedElement)) { | ||
var _getContainerConfig = (0, _container.getContainerConfig)(lastFocusedElement); | ||
lastFocusedElement = _getContainerConfig.lastFocusedElement; | ||
} | ||
if (!Spotlight.focus(lastFocusedElement)) { | ||
// If the last focused element was previously also disabled (or no longer exists), we | ||
@@ -628,2 +632,3 @@ // need to set focus somewhere | ||
* | ||
* @function | ||
* @returns {undefined} | ||
@@ -637,2 +642,3 @@ * @public | ||
* | ||
* @function | ||
* @returns {undefined} | ||
@@ -803,2 +809,3 @@ * @public | ||
* | ||
* @function | ||
* @returns {Boolean} `true` if Spotlight is currently paused. | ||
@@ -805,0 +812,0 @@ * @public |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
267256
5402
1
Updated@enact/core@^2.2.0