Socket
Socket
Sign inDemoInstall

@chakra-ui/descendant

Package Overview
Dependencies
Maintainers
4
Versions
175
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@chakra-ui/descendant - npm Package Compare versions

Comparing version 1.1.3 to 2.0.0

dist/cjs/descendant.js

20

CHANGELOG.md
# Change Log
## 2.0.0
### Major Changes
- [`82f08867f`](https://github.com/chakra-ui/chakra-ui/commit/82f08867fa4825d647a3b9cc805220d9364f2f3f)
[#3864](https://github.com/chakra-ui/chakra-ui/pull/3864) Thanks
[@segunadebayo](https://github.com/segunadebayo)! - > Note: This is an
internal package.
Improve performance of registering, filtering and finding the index of
descendants in a disclosure(open/close) widget.
Mostly used by the accordion, menu, tabs and pin-input components.
### Patch Changes
- Updated dependencies
[[`82f08867f`](https://github.com/chakra-ui/chakra-ui/commit/82f08867fa4825d647a3b9cc805220d9364f2f3f)]:
- @chakra-ui/react-utils@1.1.2
## 1.1.3

@@ -4,0 +24,0 @@

@@ -12,2 +12,10 @@ "use strict";

});
var _descendant = require("./descendant");
Object.keys(_descendant).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _descendant[key]) return;
exports[key] = _descendant[key];
});
//# sourceMappingURL=index.js.map

178

dist/cjs/use-descendant.js
"use strict";
exports.__esModule = true;
exports.useDescendants = useDescendants;
exports.useDescendant = useDescendant;
exports.useDescendants = useDescendants;
exports.createDescendantContext = createDescendantContext;
exports.useDescendantsContext = exports.DescendantsContextProvider = void 0;
var _reactUtils = require("@chakra-ui/react-utils");
var _react = require("react");
var _hooks = require("@chakra-ui/hooks");
var _descendant = require("./descendant");
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
var _utils = require("./utils");
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
/**
* @internal
* React hook that initializes the DescendantsManager
*/
function useDescendants() {
var _useState = (0, _react.useState)(function () {
return new _descendant.DescendantsManager();
}),
descendants = _useState[0];
function useDescendant(props) {
var context = props.context,
element = props.element,
indexProp = props.index,
disabled = props.disabled,
focusable = props.focusable,
rest = _objectWithoutPropertiesLoose(props, ["context", "element", "index", "disabled", "focusable"]);
(0, _utils.useSafeLayoutEffect)(function () {
return function () {
return descendants.destroy();
};
});
return descendants;
}
var forceUpdate = (0, _hooks.useForceUpdate)();
var register = context.register,
unregister = context.unregister,
descendants = context.descendants;
(0, _hooks.useSafeLayoutEffect)(function () {
if (!element) {
forceUpdate();
}
/**
* Don't register this descendant if it is disabled and not focusable
*/
/* -------------------------------------------------------------------------------------------------
* Descendants context to be used in component-land.
- Mount the `DescendantsContextProvider` at the root of the component
- Call `useDescendantsContext` anywhere you need access to the descendants information
NB: I recommend using `createDescendantContext` below
* -----------------------------------------------------------------------------------------------*/
var _createContext = (0, _reactUtils.createContext)({
name: "DescendantsProvider",
errorMessage: "useDescendantsContext must be used within DescendantsProvider"
}),
DescendantsContextProvider = _createContext[0],
useDescendantsContext = _createContext[1];
/**
* @internal
* This hook provides information a descendant such as:
* - Its index compared to other descendants
* - ref callback to register the descendant
* - Its enabled index compared to other enabled descendants
*/
if (disabled && !focusable) return undefined;
/**
* else, register the descendant
*/
register(_extends({
element: element,
disabled: disabled,
focusable: focusable
}, rest));
/**
* when it unmounts, unregister the descendant
*/
exports.useDescendantsContext = useDescendantsContext;
exports.DescendantsContextProvider = DescendantsContextProvider;
function useDescendant(options) {
var descendants = useDescendantsContext();
var _useState2 = (0, _react.useState)(-1),
index = _useState2[0],
setIndex = _useState2[1];
var ref = (0, _react.useRef)(null);
(0, _utils.useSafeLayoutEffect)(function () {
return function () {
if (element) {
unregister(element);
}
}; // eslint-disable-next-line
}, [element, disabled, focusable].concat(Object.values(rest)));
var index = indexProp != null ? indexProp : descendants.findIndex(function (descendant) {
return descendant.element === element;
if (!ref.current) return;
descendants.unregister(ref.current);
};
}, []);
(0, _utils.useSafeLayoutEffect)(function () {
if (!ref.current) return;
var dataIndex = Number(ref.current.dataset.index);
if (index != dataIndex && !Number.isNaN(dataIndex)) {
setIndex(dataIndex);
}
});
return index;
var refCallback = options ? (0, _utils.cast)(descendants.register(options)) : (0, _utils.cast)(descendants.register);
return {
descendants: descendants,
index: index,
enabledIndex: descendants.enabledIndexOf(ref.current),
register: (0, _reactUtils.mergeRefs)(refCallback, ref)
};
}
/* -------------------------------------------------------------------------------------------------
* Function that provides strongly typed versions of the context provider and hooks above.
To be used in component-land
* -----------------------------------------------------------------------------------------------*/
function useDescendants() {
var _useState = (0, _react.useState)([]),
descendants = _useState[0],
setDescendants = _useState[1];
var register = (0, _react.useCallback)(function (_ref) {
var element = _ref.element,
rest = _objectWithoutPropertiesLoose(_ref, ["element"]);
function createDescendantContext() {
var ContextProvider = (0, _utils.cast)(DescendantsContextProvider);
if (!element) return; // @ts-ignore
var _useDescendantsContext = function _useDescendantsContext() {
return (0, _utils.cast)(useDescendantsContext());
};
setDescendants(function (prevDescendants) {
if (prevDescendants.find(function (item) {
return item.element === element;
}) == null) {
var index = prevDescendants.findIndex(function (item) {
if (!item.element || !element) return false;
return Boolean(item.element.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_PRECEDING);
});
var _useDescendant = function _useDescendant(options) {
return useDescendant(options);
};
var newItem = _extends({
element: element
}, rest);
var _useDescendants = function _useDescendants() {
return useDescendants();
};
if (index === -1) {
return [].concat(prevDescendants, [newItem]);
}
return [].concat(prevDescendants.slice(0, index), [newItem], prevDescendants.slice(index));
}
return prevDescendants;
});
}, []);
var unregister = (0, _react.useCallback)(function (element) {
if (!element) return;
setDescendants(function (descendants) {
return descendants.filter(function (descendant) {
return element !== descendant.element;
});
});
}, []);
var context = (0, _react.useMemo)(function () {
return {
descendants: descendants,
register: register,
unregister: unregister
};
}, [descendants, register, unregister]);
return context;
return [// context provider
ContextProvider, // call this when you need to read from context
_useDescendantsContext, // descendants state information, to be called and passed to `ContextProvider`
_useDescendants, // descendant index information
_useDescendant];
}
//# sourceMappingURL=use-descendant.js.map
export * from "./use-descendant";
export * from "./descendant";
//# sourceMappingURL=index.js.map

@@ -1,97 +0,83 @@

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import { createContext, mergeRefs } from "@chakra-ui/react-utils";
import { useRef, useState } from "react";
import { DescendantsManager } from "./descendant";
import { useSafeLayoutEffect, cast } from "./utils";
/**
* @internal
* React hook that initializes the DescendantsManager
*/
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import { useCallback, useMemo, useState } from "react";
import { useSafeLayoutEffect, useForceUpdate } from "@chakra-ui/hooks";
export function useDescendant(props) {
var {
context,
element,
index: indexProp,
disabled,
focusable
} = props,
rest = _objectWithoutPropertiesLoose(props, ["context", "element", "index", "disabled", "focusable"]);
var forceUpdate = useForceUpdate();
var {
register,
unregister,
descendants
} = context;
export function useDescendants() {
var [descendants] = useState(() => new DescendantsManager());
useSafeLayoutEffect(() => {
if (!element) {
forceUpdate();
}
/**
* Don't register this descendant if it is disabled and not focusable
*/
return () => descendants.destroy();
});
return descendants;
}
/* -------------------------------------------------------------------------------------------------
* Descendants context to be used in component-land.
- Mount the `DescendantsContextProvider` at the root of the component
- Call `useDescendantsContext` anywhere you need access to the descendants information
if (disabled && !focusable) return undefined;
/**
* else, register the descendant
*/
NB: I recommend using `createDescendantContext` below
* -----------------------------------------------------------------------------------------------*/
export var [DescendantsContextProvider, useDescendantsContext] = createContext({
name: "DescendantsProvider",
errorMessage: "useDescendantsContext must be used within DescendantsProvider"
});
/**
* @internal
* This hook provides information a descendant such as:
* - Its index compared to other descendants
* - ref callback to register the descendant
* - Its enabled index compared to other enabled descendants
*/
register(_extends({
element,
disabled,
focusable
}, rest));
/**
* when it unmounts, unregister the descendant
*/
export function useDescendant(options) {
var descendants = useDescendantsContext();
var [index, setIndex] = useState(-1);
var ref = useRef(null);
useSafeLayoutEffect(() => {
return () => {
if (!ref.current) return;
descendants.unregister(ref.current);
};
}, []);
useSafeLayoutEffect(() => {
if (!ref.current) return;
var dataIndex = Number(ref.current.dataset.index);
return () => {
if (element) {
unregister(element);
}
}; // eslint-disable-next-line
}, [element, disabled, focusable, ...Object.values(rest)]);
var index = indexProp != null ? indexProp : descendants.findIndex(descendant => descendant.element === element);
return index;
if (index != dataIndex && !Number.isNaN(dataIndex)) {
setIndex(dataIndex);
}
});
var refCallback = options ? cast(descendants.register(options)) : cast(descendants.register);
return {
descendants,
index,
enabledIndex: descendants.enabledIndexOf(ref.current),
register: mergeRefs(refCallback, ref)
};
}
export function useDescendants() {
var [descendants, setDescendants] = useState([]);
var register = useCallback((_ref) => {
var {
element
} = _ref,
rest = _objectWithoutPropertiesLoose(_ref, ["element"]);
/* -------------------------------------------------------------------------------------------------
* Function that provides strongly typed versions of the context provider and hooks above.
To be used in component-land
* -----------------------------------------------------------------------------------------------*/
if (!element) return; // @ts-ignore
export function createDescendantContext() {
var ContextProvider = cast(DescendantsContextProvider);
setDescendants(prevDescendants => {
if (prevDescendants.find(item => item.element === element) == null) {
var index = prevDescendants.findIndex(item => {
if (!item.element || !element) return false;
return Boolean(item.element.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_PRECEDING);
});
var _useDescendantsContext = () => cast(useDescendantsContext());
var newItem = _extends({
element
}, rest);
var _useDescendant = options => useDescendant(options);
if (index === -1) {
return [...prevDescendants, newItem];
}
var _useDescendants = () => useDescendants();
return [...prevDescendants.slice(0, index), newItem, ...prevDescendants.slice(index)];
}
return prevDescendants;
});
}, []);
var unregister = useCallback(element => {
if (!element) return;
setDescendants(descendants => descendants.filter(descendant => element !== descendant.element));
}, []);
var context = useMemo(() => ({
descendants,
register,
unregister
}), [descendants, register, unregister]);
return context;
return [// context provider
ContextProvider, // call this when you need to read from context
_useDescendantsContext, // descendants state information, to be called and passed to `ContextProvider`
_useDescendants, // descendant index information
_useDescendant];
}
//# sourceMappingURL=use-descendant.js.map
export * from "./use-descendant";
export * from "./descendant";
//# sourceMappingURL=index.d.ts.map

@@ -1,21 +0,30 @@

export declare type Descendant<T extends HTMLElement, P = {}> = P & {
element: T | null;
index?: number;
disabled?: boolean;
focusable?: boolean;
};
export interface DescendantContext<T extends HTMLElement, P = {}> {
descendants: Descendant<T, P>[];
register: (descendant: Descendant<T, P>) => void;
unregister: (element: T) => void;
/// <reference types="react" />
import { DescendantsManager, DescendantOptions } from "./descendant";
/**
* @internal
* React hook that initializes the DescendantsManager
*/
export declare function useDescendants<T extends HTMLElement = HTMLElement, K = {}>(): DescendantsManager<T, K>;
export interface UseDescendantsReturn extends ReturnType<typeof useDescendants> {
}
export declare type UseDescendantProps<T extends HTMLElement, P> = {
context: DescendantContext<T, P>;
} & Descendant<T, P>;
export declare function useDescendant<T extends HTMLElement, P>(props: UseDescendantProps<T, P>): number;
export declare function useDescendants<T extends HTMLElement, P>(): {
descendants: Descendant<T, P>[];
register: ({ element, ...rest }: Descendant<T, P>) => void;
unregister: (element: T) => void;
export declare const DescendantsContextProvider: import("react").Provider<UseDescendantsReturn>, useDescendantsContext: () => UseDescendantsReturn;
/**
* @internal
* This hook provides information a descendant such as:
* - Its index compared to other descendants
* - ref callback to register the descendant
* - Its enabled index compared to other enabled descendants
*/
export declare function useDescendant<T extends HTMLElement = HTMLElement, K = {}>(options?: DescendantOptions & K): {
descendants: UseDescendantsReturn;
index: number;
enabledIndex: number;
register: (node: T | null) => void;
};
export declare function createDescendantContext<T extends HTMLElement = HTMLElement, K = {}>(): readonly [import("react").Provider<DescendantsManager<T, K>>, () => DescendantsManager<T, K>, () => DescendantsManager<T, K>, (options?: (DescendantOptions & K) | undefined) => {
descendants: UseDescendantsReturn;
index: number;
enabledIndex: number;
register: (node: T | null) => void;
}];
//# sourceMappingURL=use-descendant.d.ts.map
{
"name": "@chakra-ui/descendant",
"version": "1.1.3",
"version": "2.0.0",
"description": "Register child nodes of a react element for better accessibility",

@@ -57,3 +57,3 @@ "keywords": [

"dependencies": {
"@chakra-ui/hooks": "1.5.0"
"@chakra-ui/react-utils": "^1.1.2"
},

@@ -60,0 +60,0 @@ "peerDependencies": {

# Descendant
Keep track of descendant components and their relative indices.
A descendant index solution for better accessibility support in compound

@@ -12,7 +14,133 @@ components.

```sh
yarn add @chakra-ui/descendants
yarn add @chakra-ui/descendant
# or
npm i @chakra-ui/descendants
npm i @chakra-ui/descendant
```
## Motivation
Descendants observer is an utility hook for keeping track of descendant elements
and their relative indices.
In short, this package allows each item in a list to know it's relative index
and the parent of the list can keep track of each child, without needing to
render in a loop and pass each component an index.
This enables component composition:
```jsx
<List>
<Item /> // I'm index 0
<Item /> // I'm index 1<div>
<div>
<Item /> // I'm arbitrarily nested, but still know that I'm index 2
</div>
</div>
</List>
```
### Usage
```jsx
import { createDescendantContext } from "@descendants/react"
import * as React from "react"
const [
DescendantsProvider,
useDescendantsContext,
useDescendants,
useDescendant,
] = createDescendantContext()
const MenuContext = React.createContext({})
function Menu({ children }) {
// 1. Call the `useDescendants` hook
const descendants = useDescendants()
const [selected, setSelected] = React.useState(1)
const context = React.useMemo(() => ({ selected, setSelected }), [selected])
return (
// 2. Add the descendants context
<DescendantsProvider value={descendants}>
<MenuContext.Provider value={context}>
<div role="menu" style={{ maxWidth: 320 }}>
<button
onClick={() => {
const prev = descendants.prev(selected)
prev.node.focus()
setSelected(prev.index)
}}
>
Prev
</button>
<button
onClick={() => {
const next = descendants.next(selected)
next.node.focus()
setSelected(next.index)
}}
>
Next
</button>
{children}
</div>
</MenuContext.Provider>
</DescendantsProvider>
)
}
const MenuItem = ({ children }) => {
const { selected, setSelected } = React.useContext(MenuContext)
// 3. Read from descendant context
const { index, register } = useDescendant()
const isSelected = index === selected
return (
<div
role="menuitem"
ref={register}
aria-selected={isSelected}
onMouseMove={() => setSelected(index)}
style={{ color: isSelected ? "red" : "black" }}
>
{children} - {index}
</div>
)
}
const Example = () => {
const [show, setShow] = React.useState(false)
const [show2, setShow2] = React.useState(false)
const toggle = () => {
setShow(!show)
if (!show === true) {
setTimeout(() => {
setShow2(true)
}, 1000)
}
}
return (
<div>
<button onClick={toggle}>Toggle</button>
<Menu>
<MenuItem>One</MenuItem>
{show && <MenuItem>Two</MenuItem>}
<MenuItem>Three</MenuItem>
<MenuItem>Four</MenuItem>
<div>
{show2 && <MenuItem>Testing 🌟</MenuItem>}
<MenuItem>Five</MenuItem>
</div>
</Menu>
</div>
)
}
```

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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