Socket
Socket
Sign inDemoInstall

react-focus-lock

Package Overview
Dependencies
Maintainers
1
Versions
106
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-focus-lock - npm Package Compare versions

Comparing version 2.9.8 to 2.10.0

dist/cjs/nano-events.js

24

dist/cjs/Lock.js

@@ -20,2 +20,3 @@ "use strict";

var _medium = require("./medium");
var _scope = require("./scope");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }

@@ -34,2 +35,5 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }

var originalFocusedElement = React.useRef(null);
var _React$useState3 = React.useState({}),
_React$useState4 = (0, _slicedToArray2["default"])(_React$useState3, 2),
update = _React$useState4[1];
var children = props.children,

@@ -63,5 +67,5 @@ _props$disabled = props.disabled,

onDeactivationCallback = props.onDeactivation;
var _React$useState3 = React.useState({}),
_React$useState4 = (0, _slicedToArray2["default"])(_React$useState3, 1),
id = _React$useState4[0];
var _React$useState5 = React.useState({}),
_React$useState6 = (0, _slicedToArray2["default"])(_React$useState5, 1),
id = _React$useState6[0];
var onActivation = React.useCallback(function () {

@@ -73,2 +77,3 @@ originalFocusedElement.current = originalFocusedElement.current || document && document.activeElement;

isActive.current = true;
update();
}, [onActivationCallback]);

@@ -80,2 +85,3 @@ var onDeactivation = React.useCallback(function () {

}
update();
}, [onDeactivationCallback]);

@@ -130,2 +136,10 @@ (0, _react.useEffect)(function () {

var mergedRef = (0, _useCallbackRef.useMergeRefs)([parentRef, setObserveNode]);
var focusScopeValue = React.useMemo(function () {
return {
observed: observed,
shards: shards,
enabled: !disabled,
active: isActive.current
};
}, [disabled, isActive.current, shards, realObserved]);
return /*#__PURE__*/React.createElement(React.Fragment, null, hasLeadingGuards && [

@@ -163,3 +177,5 @@ /*#__PURE__*/

onFocus: onFocus
}), children), hasTailingGuards && /*#__PURE__*/React.createElement("div", {
}), /*#__PURE__*/React.createElement(_scope.focusScope.Provider, {
value: focusScopeValue
}, children)), hasTailingGuards && /*#__PURE__*/React.createElement("div", {
"data-focus-guard": true,

@@ -166,0 +182,0 @@ tabIndex: disabled ? -1 : 0,

3

dist/cjs/medium.js

@@ -19,3 +19,4 @@ "use strict";

var mediumSidecar = exports.mediumSidecar = (0, _useSidecar.createSidecarMedium)({
async: true
async: true,
ssr: typeof document !== 'undefined'
});

@@ -65,5 +65,2 @@ "use strict";

}
var extractRef = function extractRef(ref) {
return ref && 'current' in ref ? ref.current : ref;
};
var focusWasOutside = function focusWasOutside(crossFrameOption) {

@@ -96,3 +93,3 @@ if (crossFrameOption) {

if (workingNode) {
var workingArea = [workingNode].concat((0, _toConsumableArray2["default"])(shards.map(extractRef).filter(Boolean)));
var workingArea = [workingNode].concat((0, _toConsumableArray2["default"])(shards.map(_util.extractRef).filter(Boolean)));
if (!activeElement || focusWhitelisted(activeElement)) {

@@ -226,5 +223,7 @@ if (persistentFocus || focusWasOutside(crossFrame) || !isFreeFocus() || !lastActiveFocus && autoFocus) {

moveFocusInside: _focusLock.moveFocusInside,
focusInside: _focusLock.focusInside
focusInside: _focusLock.focusInside,
focusNextElement: _focusLock.focusNextElement,
focusPrevElement: _focusLock.focusPrevElement
});
});
var _default = exports["default"] = (0, _reactClientsideEffect["default"])(reducePropsToState, handleStateChangeOnClient)(FocusWatcher);

@@ -39,2 +39,8 @@ "use strict";

exports["default"] = void 0;
Object.defineProperty(exports, "useFocusController", {
enumerable: true,
get: function get() {
return _useFocusScope.useFocusController;
}
});
Object.defineProperty(exports, "useFocusInside", {

@@ -46,2 +52,14 @@ enumerable: true,

});
Object.defineProperty(exports, "useFocusScope", {
enumerable: true,
get: function get() {
return _useFocusScope.useFocusScope;
}
});
Object.defineProperty(exports, "useFocusState", {
enumerable: true,
get: function get() {
return _useFocusState.useFocusState;
}
});
var _Lock = _interopRequireDefault(require("./Lock"));

@@ -52,4 +70,6 @@ var _AutoFocusInside = _interopRequireDefault(require("./AutoFocusInside"));

var _FocusGuard = _interopRequireDefault(require("./FocusGuard"));
var _useFocusScope = require("./use-focus-scope");
var _useFocusState = require("./use-focus-state");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
var _default = exports["default"] = _Lock["default"];

@@ -7,3 +7,3 @@ "use strict";

exports.deferAction = deferAction;
exports.inlineProp = void 0;
exports.inlineProp = exports.extractRef = void 0;
function deferAction(action) {

@@ -16,2 +16,5 @@ setTimeout(action, 1);

return obj;
};
var extractRef = exports.extractRef = function extractRef(ref) {
return ref && 'current' in ref ? ref.current : ref;
};

@@ -9,2 +9,3 @@ import _extends from "@babel/runtime/helpers/esm/extends";

import { mediumFocus, mediumBlur, mediumSidecar } from './medium';
import { focusScope } from './scope';
var emptyArray = [];

@@ -19,2 +20,4 @@ var FocusLock = /*#__PURE__*/React.forwardRef(function FocusLockUI(props, parentRef) {

var originalFocusedElement = React.useRef(null);
var _React$useState2 = React.useState({}),
update = _React$useState2[1];
var children = props.children,

@@ -48,4 +51,4 @@ _props$disabled = props.disabled,

onDeactivationCallback = props.onDeactivation;
var _React$useState2 = React.useState({}),
id = _React$useState2[0];
var _React$useState3 = React.useState({}),
id = _React$useState3[0];
var onActivation = React.useCallback(function () {

@@ -57,2 +60,3 @@ originalFocusedElement.current = originalFocusedElement.current || document && document.activeElement;

isActive.current = true;
update();
}, [onActivationCallback]);

@@ -64,2 +68,3 @@ var onDeactivation = React.useCallback(function () {

}
update();
}, [onDeactivationCallback]);

@@ -114,2 +119,10 @@ useEffect(function () {

var mergedRef = useMergeRefs([parentRef, setObserveNode]);
var focusScopeValue = React.useMemo(function () {
return {
observed: observed,
shards: shards,
enabled: !disabled,
active: isActive.current
};
}, [disabled, isActive.current, shards, realObserved]);
return /*#__PURE__*/React.createElement(React.Fragment, null, hasLeadingGuards && [

@@ -147,3 +160,5 @@ /*#__PURE__*/

onFocus: onFocus
}), children), hasTailingGuards && /*#__PURE__*/React.createElement("div", {
}), /*#__PURE__*/React.createElement(focusScope.Provider, {
value: focusScopeValue
}, children)), hasTailingGuards && /*#__PURE__*/React.createElement("div", {
"data-focus-guard": true,

@@ -150,0 +165,0 @@ tabIndex: disabled ? -1 : 0,

@@ -13,3 +13,4 @@ import { createMedium, createSidecarMedium } from 'use-sidecar';

export var mediumSidecar = createSidecarMedium({
async: true
async: true,
ssr: typeof document !== 'undefined'
});
import * as React from 'react';
import PropTypes from 'prop-types';
import withSideEffect from 'react-clientside-effect';
import { moveFocusInside, focusInside, focusIsHidden, expandFocusableNodes } from 'focus-lock';
import { deferAction } from './util';
import { moveFocusInside, focusInside, focusIsHidden, expandFocusableNodes, focusNextElement, focusPrevElement } from 'focus-lock';
import { deferAction, extractRef } from './util';
import { mediumFocus, mediumBlur, mediumEffect } from './medium';

@@ -54,5 +54,2 @@ var focusOnBody = function focusOnBody() {

}
var extractRef = function extractRef(ref) {
return ref && 'current' in ref ? ref.current : ref;
};
var focusWasOutside = function focusWasOutside(crossFrameOption) {

@@ -214,5 +211,7 @@ if (crossFrameOption) {

moveFocusInside: moveFocusInside,
focusInside: focusInside
focusInside: focusInside,
focusNextElement: focusNextElement,
focusPrevElement: focusPrevElement
});
});
export default withSideEffect(reducePropsToState, handleStateChangeOnClient)(FocusWatcher);

@@ -6,3 +6,5 @@ import FocusLockUI from './Lock';

import InFocusGuard from './FocusGuard';
export { AutoFocusInside, MoveFocusInside, FreeFocusInside, InFocusGuard, FocusLockUI, useFocusInside };
import { useFocusController, useFocusScope } from './use-focus-scope';
import { useFocusState } from './use-focus-state';
export { AutoFocusInside, MoveFocusInside, FreeFocusInside, InFocusGuard, FocusLockUI, useFocusInside, useFocusController, useFocusScope, useFocusState };
export default FocusLockUI;

@@ -8,2 +8,5 @@ export function deferAction(action) {

return obj;
};
export var extractRef = function extractRef(ref) {
return ref && 'current' in ref ? ref.current : ref;
};
{
"name": "react-focus-lock",
"version": "2.9.8",
"version": "2.10.0",
"description": "It is a trap! (for a focus)",

@@ -104,3 +104,3 @@ "main": "dist/cjs/index.js",

"@babel/runtime": "^7.0.0",
"focus-lock": "^1.0.1",
"focus-lock": "^1.1.0",
"prop-types": "^15.6.2",

@@ -107,0 +107,0 @@ "react-clientside-effect": "^1.2.6",

@@ -23,2 +23,3 @@ <div align="left">

- Any any other case, when you have to lock user _intention_ and _focus_, if that's what `a11y` is asking for.
- Including programatic focus management and smart return focus

@@ -34,9 +35,9 @@ ### Trusted

# Features
- no keyboard control, everything is done watching a __focus behavior__, not emulating it. Thus works always and everywhere.
- no keyboard control, everything is done watching a __focus behavior__, not emulating it. Focus-Locks works for all cases including positive tab indexes.
- React __Portals__ support. Even if some data is in outer space - it is [still in lock](https://github.com/theKashey/react-focus-lock/issues/19).
- _Scattered_ locks, or focus lock groups - you can setup different isolated locks, and _tab_ from one to another.
- Controllable isolation level.
- variable size bundle. Uses sidecar to trim UI part to 1.5kb.
- variable size bundle. Uses _sidecar_ to trim UI part down to 1.5kb.
> 💡 __focus__ locks is only the first part, there are also __scroll lock__ and __text-to-speech__ lock
> 💡 __focus__ locks is part of a bigger whole, consider __scroll lock__ and __text-to-speech__ lock
you have to use to really "lock" the user.

@@ -59,11 +60,2 @@ Try [react-focus-on](https://github.com/theKashey/react-focus-on) to archive everything above, assembled in the right order.

# WHY?
From [MDN Article about accessible dialogs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_dialog_role):
- The dialog must be properly labeled
- Keyboard __focus must be managed__ correctly
This one is about managing the focus.
I've got a good [article about focus management, dialogs and WAI-ARIA](https://medium.com/@antonkorzunov/its-a-focus-trap-699a04d66fb5).
# API

@@ -90,2 +82,21 @@ > FocusLock would work perfectly even with no props set.

## Programmatic control
Focus lock exposes a few methods to control focus programmatically.
### Imperative API
- `useFocusInside(nodeRef)` - to move focus inside the given node
- `useFocusScope():{autofocus, focusNext, focusPrev}` - provides API to manage focus within the current lock
- `useFocusState()` - manages focus state of a given node
- `useFocusController(nodeRef)` - low level version of `useFocusScope` working without FocusLock
### Declarative API
- `<AutoFocusInside/>` - causes autofocus to look inside the component
- `<MoveFocusInside/>` - wrapper around `useFocusInside`, forcibly moves focus inside on mount
- `<FreeFocusInside/>` - hides internals from FocusLock allowing unmanaged focus
#### Indirect API
Focus-lock behavior can be controlled via data-attributes. Declarative API above is working by setting them for you.
See [corresponding section in focus-lock](https://github.com/theKashey/focus-lock#declarative-control) for details
### Focusing in OSX (Safari/Firefox) is strange!

@@ -225,2 +236,52 @@ By default `tabbing` in OSX `sees` only controls, but not links or anything else `tabbable`. This is system settings, and Safari/Firefox obey.

### Programmatic Control
Let's take a look at the `Rowing Focus` as an example.
```tsx
// Will set tabindex to -1 when is not focused
const FocusTrackingButton = ({ children }) => {
const { active, onFocus, ref } = useFocusState();
return (
<button tabIndex={active ? undefined : -1} onFocus={onFocus} ref={ref}>
{children}
</button>
);
};
const RowingFocusInternalTrap = () => {
const { autoFocus, focusNext, focusPrev } = useFocusScope();
// use useFocusController(divRef) if there is no FocusLock around
useEffect(() => {
autoFocus();
}, []);
const onKey = (event) => {
if (event.key === 'ArrowDown') {
focusNext({ onlyTabbable: false });
}
if (event.key === 'ArrowUp') {
focusPrev({ onlyTabbable: false });
}
};
return (
<div
onKeyDown={onKey}
// ref={divRef} for useFocusController
>
<FocusButton>Button1</FocusButton>
<FocusButton>Button2</FocusButton>
<FocusButton>Button3</FocusButton>
<FocusButton>Button4</FocusButton>
</div>
);
};
// FocusLock, even disabled one
const RowingFocusTrap = () => (
<FocusLock disabled>
<RowingFocusInternalTrap />
</FocusLock>
);
```
### Guarding

@@ -288,14 +349,11 @@ As you may know - FocusLock is adding `Focus Guards` before and after lock to remove some side effects, like page scrolling.

# Not only for React
Uses [focus-lock](https://github.com/theKashey/focus-lock/) under the hood. It does also provide support for Vue.js and Vanilla DOM solutions
# Warning!
Two different _focus-lock-managers_ or even different version of a single one, active
simultaneously will FIGHT!
## Focus fighting
Two different _focus-lock-managers_ or even different version of a single one, being active
simultaneously will FIGHT for the focus. This usually totally breaks user experience.
__Focus-lock will surrender__, as long any other focus management library will not.
__React-Focus-Lock will automatically surrender__, letting another library to take the lead.
## Focus fighting
### Resolving focus fighting
You may wrap some render branch with `FreeFocusInside`, and react-focus-lock __will ignore__
any focus inside marked node, thus landing a peace.
any focus inside marked node. So in case focus moves to _uncontrolled location_ focus-lock will not trigger letting another library to act without interference in that another location.

@@ -311,4 +369,4 @@ ```js

```
Even the better is to `whiteList` FocusLock areas - for example "you should handle only React Stuff in React Root"
Another option for hybrid applications is to `whiteList` area where Focus-Lock should act, automatically allowing other managers in other areas.
The code below will scope Focus-Lock on inside the (react)`root` element, so anything jQuery can add to the body will be ignored.
```js

@@ -320,3 +378,5 @@ <FocusLock whiteList={node => document.getElementById('root').contains(node)}>

PS: __please use webpack or yarn resolution for force one version of react-focus-lock used__
### Two Focus-Locks
React-Focus-Lock is expected to be a singlentone.
__Use webpack or yarn resolution for force only one version of react-focus-lock used.

@@ -331,2 +391,14 @@ > webpack.conf

# WHY?
From [MDN Article about accessible dialogs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_dialog_role):
- The dialog must be properly labeled
- Keyboard __focus must be managed__ correctly
This one is about managing the focus.
I've got a good [article about focus management, dialogs and WAI-ARIA](https://medium.com/@antonkorzunov/its-a-focus-trap-699a04d66fb5).
# Not only for React
Uses [focus-lock](https://github.com/theKashey/focus-lock/) under the hood. It does also provide support for Vue.js and Vanilla DOM solutions
# More

@@ -333,0 +405,0 @@ To create a "right" modal dialog you have to:

@@ -38,2 +38,72 @@ import * as React from 'react';

*/
export function useFocusInside(node: React.RefObject<HTMLElement>): void;
export function useFocusInside(node: React.RefObject<HTMLElement>): void;
export type FocusOptions = {
/**
* enables focus cycle
* @default true
*/
cycle?: boolean;
/**
* limits focusables to tabbables (tabindex>=0) elements only
* @default true
*/
onlyTabbable?:boolean
}
export type FocusControl = {
/**
* moves focus to the current scope, can be considered as autofocus
*/
autoFocus():Promise<void>;
/**
* focuses the next element in the scope.
* If active element is not in the scope, autofocus will be triggered first
*/
focusNext(options:FocusOptions):Promise<void>;
/**
* focuses the prev element in the scope.
* If active element is not in the scope, autofocus will be triggered first
*/
focusPrev():Promise<void>;
}
/**
* returns FocusControl over the union given elements, one or many
* - can be used outside of FocusLock
* @see {@link useFocusScope} for use cases inside of FocusLock
*/
export function useFocusController(...shards: HTMLElement[]):FocusControl;
/**
* returns FocusControl over the current FocusLock
* - can be used only within FocusLock
* - can be used by disabled FocusLock
* @see {@link useFocusController} for use cases outside of FocusLock
*/
export function useFocusScope():FocusControl
/**
* returns information about FocusState of a given node
* @example
* ```tsx
* const {active, ref, onFocus} = useFocusState();
* return <div ref={ref} onFocus={onFocus}>{active ? 'is focused' : 'not focused'}</div>
* ```
*/
export function useFocusState<T extends Element>():{
/**
* is currently focused, or is focus is inside
*/
active: boolean;
/**
* focus handled. SHALL be passed to the node down
*/
onFocus: React.FocusEventHandler<T>;
/**
* reference to the node
* only required to capture current status of the node
*/
ref: React.RefObject<T>;
}
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