1
This commit is contained in:
+9
@@ -0,0 +1,9 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019-present afc163
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# rc-resize-observer
|
||||
|
||||
[![NPM version][npm-image]][npm-url] [](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![npm download][download-image]][download-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/rc-resize-observer.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/rc-resize-observer
|
||||
[github-actions-image]: https://github.com/react-component/resize-observer/workflows/CI/badge.svg
|
||||
[github-actions-url]: https://github.com/react-component/resize-observer/actions
|
||||
[codecov-image]: https://img.shields.io/codecov/c/github/react-component/resize-observer/master.svg?style=flat-square
|
||||
[codecov-url]: https://codecov.io/gh/react-component/resize-observer/branch/master
|
||||
[download-image]: https://img.shields.io/npm/dm/rc-resize-observer.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/rc-resize-observer
|
||||
|
||||
Resize observer for React.
|
||||
|
||||
## Live Demo
|
||||
|
||||
https://resize-observer-react-component.vercel.app/
|
||||
|
||||
## Install
|
||||
|
||||
[](https://npmjs.org/package/rc-resize-observer)
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import ResizeObserver from 'rc-resize-observer';
|
||||
import { render } from 'react-dom';
|
||||
|
||||
render(
|
||||
<ResizeObserver
|
||||
onResize={() => {
|
||||
console.log('resized!');
|
||||
}}
|
||||
>
|
||||
<textarea />
|
||||
</ResizeObserver>,
|
||||
mountNode,
|
||||
);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
| -------- | --------------------------- | ------- | ------------------------------- |
|
||||
| disabled | boolean | false | |
|
||||
| onResize | ({ width, height }) => void | - | Trigger when child node resized |
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
rc-resize-observer is released under the MIT license.
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
.rc-footer {
|
||||
position: relative;
|
||||
clear: both;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
background-color: #000;
|
||||
}
|
||||
.rc-footer a {
|
||||
transition: all 0.3s;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-decoration: none;
|
||||
}
|
||||
.rc-footer a:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
.rc-footer-container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 80px 0;
|
||||
margin: auto;
|
||||
}
|
||||
.rc-footer-columns {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
.rc-footer-column h2 {
|
||||
position: relative;
|
||||
margin: 0 auto 24px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
.rc-footer-column-icon {
|
||||
margin-right: 0.5em;
|
||||
width: 22px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
top: -1px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.rc-footer-column-icon > span,
|
||||
.rc-footer-column-icon > svg,
|
||||
.rc-footer-column-icon img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
.rc-footer-item {
|
||||
margin: 12px 0;
|
||||
}
|
||||
.rc-footer-item-icon {
|
||||
margin-right: 0.4em;
|
||||
width: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
top: -1px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.rc-footer-item-icon > span,
|
||||
.rc-footer-item-icon > svg,
|
||||
.rc-footer-item-icon img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
.rc-footer-item-separator {
|
||||
margin: 0 0.3em;
|
||||
}
|
||||
.rc-footer-bottom-container {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.25);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 16px 0;
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.rc-footer-light {
|
||||
background-color: transparent;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
.rc-footer-light h2,
|
||||
.rc-footer-light a {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
.rc-footer-light .rc-footer-bottom-container {
|
||||
border-top-color: #e8e8e8;
|
||||
}
|
||||
.rc-footer-light .rc-footer-item-separator,
|
||||
.rc-footer-light .rc-footer-item-description {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
@media only screen and (max-width: 767.99px) {
|
||||
.rc-footer {
|
||||
text-align: center;
|
||||
}
|
||||
.rc-footer-container {
|
||||
padding: 40px 0;
|
||||
}
|
||||
.rc-footer-columns {
|
||||
display: block;
|
||||
}
|
||||
.rc-footer-column {
|
||||
display: block;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
.rc-footer-column:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
@footer-prefix-cls: rc-footer;
|
||||
|
||||
.@{footer-prefix-cls} {
|
||||
position: relative;
|
||||
clear: both;
|
||||
color: rgba(255, 255, 255, .4);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
background-color: #000;
|
||||
|
||||
a {
|
||||
transition: all .3s;
|
||||
color: rgba(255, 255, 255, .9);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: #40a9ff;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 80px 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&-columns {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
&-column {
|
||||
h2 {
|
||||
position: relative;
|
||||
margin: 0 auto 24px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
margin-right: .5em;
|
||||
width: 22px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
top: -1px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
> span,
|
||||
> svg,
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-item {
|
||||
margin: 12px 0;
|
||||
|
||||
&-icon {
|
||||
margin-right: .4em;
|
||||
width: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
top: -1px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
> span,
|
||||
> svg,
|
||||
img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&-separator {
|
||||
margin: 0 .3em;
|
||||
}
|
||||
}
|
||||
|
||||
&-bottom {
|
||||
&-container {
|
||||
border-top: 1px solid rgba(255, 255, 255, .25);
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 16px 0;
|
||||
line-height: 32px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-light {
|
||||
background-color: transparent;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
|
||||
h2,
|
||||
a {
|
||||
color: rgba(0, 0, 0, .85);
|
||||
}
|
||||
}
|
||||
|
||||
&-light &-bottom-container {
|
||||
border-top-color: #e8e8e8;
|
||||
}
|
||||
|
||||
&-light &-item-separator,
|
||||
&-light &-item-description {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767.99px) {
|
||||
.@{footer-prefix-cls} {
|
||||
text-align: center;
|
||||
|
||||
&-container {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
&-columns {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&-column {
|
||||
display: block;
|
||||
margin-bottom: 40px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import type { SizeInfo } from '.';
|
||||
type onCollectionResize = (size: SizeInfo, element: HTMLElement, data: any) => void;
|
||||
export declare const CollectionContext: React.Context<onCollectionResize>;
|
||||
export interface ResizeInfo {
|
||||
size: SizeInfo;
|
||||
data: any;
|
||||
element: HTMLElement;
|
||||
}
|
||||
export interface CollectionProps {
|
||||
/** Trigger when some children ResizeObserver changed. Collect by frame render level */
|
||||
onBatchResize?: (resizeInfo: ResizeInfo[]) => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
/**
|
||||
* Collect all the resize event from children ResizeObserver
|
||||
*/
|
||||
export declare function Collection({ children, onBatchResize }: CollectionProps): React.JSX.Element;
|
||||
export {};
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import * as React from 'react';
|
||||
export const CollectionContext = /*#__PURE__*/React.createContext(null);
|
||||
/**
|
||||
* Collect all the resize event from children ResizeObserver
|
||||
*/
|
||||
export function Collection({
|
||||
children,
|
||||
onBatchResize
|
||||
}) {
|
||||
const resizeIdRef = React.useRef(0);
|
||||
const resizeInfosRef = React.useRef([]);
|
||||
const onCollectionResize = React.useContext(CollectionContext);
|
||||
const onResize = React.useCallback((size, element, data) => {
|
||||
resizeIdRef.current += 1;
|
||||
const currentId = resizeIdRef.current;
|
||||
resizeInfosRef.current.push({
|
||||
size,
|
||||
element,
|
||||
data
|
||||
});
|
||||
Promise.resolve().then(() => {
|
||||
if (currentId === resizeIdRef.current) {
|
||||
onBatchResize?.(resizeInfosRef.current);
|
||||
resizeInfosRef.current = [];
|
||||
}
|
||||
});
|
||||
|
||||
// Continue bubbling if parent exist
|
||||
onCollectionResize?.(size, element, data);
|
||||
}, [onBatchResize, onCollectionResize]);
|
||||
return /*#__PURE__*/React.createElement(CollectionContext.Provider, {
|
||||
value: onResize
|
||||
}, children);
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import type { ResizeObserverProps } from '..';
|
||||
export interface SingleObserverProps extends ResizeObserverProps {
|
||||
children: React.ReactElement | ((ref: React.RefObject<Element>) => React.ReactElement);
|
||||
}
|
||||
declare const RefSingleObserver: React.ForwardRefExoticComponent<SingleObserverProps & React.RefAttributes<HTMLElement>>;
|
||||
export default RefSingleObserver;
|
||||
Generated
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
|
||||
import { supportRef, useComposeRef, getNodeRef } from "@rc-component/util/es/ref";
|
||||
import * as React from 'react';
|
||||
import { CollectionContext } from "../Collection";
|
||||
import useResizeObserver from "../useResizeObserver";
|
||||
function SingleObserver(props, ref) {
|
||||
const {
|
||||
children,
|
||||
disabled,
|
||||
onResize,
|
||||
data
|
||||
} = props;
|
||||
const elementRef = React.useRef(null);
|
||||
const onCollectionResize = React.useContext(CollectionContext);
|
||||
|
||||
// =========================== Children ===========================
|
||||
const isRenderProps = typeof children === 'function';
|
||||
const mergedChildren = isRenderProps ? children(elementRef) : children;
|
||||
|
||||
// ============================= Ref ==============================
|
||||
const canRef = !isRenderProps && /*#__PURE__*/React.isValidElement(mergedChildren) && supportRef(mergedChildren);
|
||||
const originRef = canRef ? getNodeRef(mergedChildren) : null;
|
||||
const mergedRef = useComposeRef(originRef, elementRef);
|
||||
const getDomElement = () => {
|
||||
return getDOM(elementRef.current);
|
||||
};
|
||||
React.useImperativeHandle(ref, () => getDomElement());
|
||||
|
||||
// =========================== Observe ============================
|
||||
useResizeObserver(!disabled, getDomElement, onResize, (sizeInfo, target) => {
|
||||
onCollectionResize?.(sizeInfo, target, data);
|
||||
});
|
||||
|
||||
// ============================ Render ============================
|
||||
return canRef ? /*#__PURE__*/React.cloneElement(mergedChildren, {
|
||||
ref: mergedRef
|
||||
}) : mergedChildren;
|
||||
}
|
||||
const RefSingleObserver = /*#__PURE__*/React.forwardRef(SingleObserver);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefSingleObserver.displayName = 'SingleObserver';
|
||||
}
|
||||
export default RefSingleObserver;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { Collection } from './Collection';
|
||||
import { _rs } from './utils/observerUtil';
|
||||
export {
|
||||
/** @private Test only for mock trigger resize event */
|
||||
_rs, };
|
||||
export { default as useResizeObserver } from './useResizeObserver';
|
||||
export interface SizeInfo {
|
||||
width: number;
|
||||
height: number;
|
||||
offsetWidth: number;
|
||||
offsetHeight: number;
|
||||
}
|
||||
export type OnResize = (size: SizeInfo, element: HTMLElement) => void;
|
||||
export interface ResizeObserverProps {
|
||||
/** Pass to ResizeObserver.Collection with additional data */
|
||||
data?: any;
|
||||
children: React.ReactNode | ((ref: React.RefObject<any>) => React.ReactElement);
|
||||
disabled?: boolean;
|
||||
/** Trigger if element resized. Will always trigger when first time render. */
|
||||
onResize?: OnResize;
|
||||
}
|
||||
declare const RefResizeObserver: React.ForwardRefExoticComponent<ResizeObserverProps & React.RefAttributes<any>> & {
|
||||
Collection: typeof Collection;
|
||||
};
|
||||
export default RefResizeObserver;
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : 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 * as React from 'react';
|
||||
import toArray from "@rc-component/util/es/Children/toArray";
|
||||
import { warning } from "@rc-component/util/es/warning";
|
||||
import SingleObserver from "./SingleObserver";
|
||||
import { Collection } from "./Collection";
|
||||
const INTERNAL_PREFIX_KEY = 'rc-observer-key';
|
||||
import { _rs } from "./utils/observerUtil";
|
||||
export { /** @private Test only for mock trigger resize event */
|
||||
_rs };
|
||||
export { default as useResizeObserver } from "./useResizeObserver";
|
||||
function ResizeObserver(props, ref) {
|
||||
const {
|
||||
children
|
||||
} = props;
|
||||
const childNodes = typeof children === 'function' ? [children] : toArray(children);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (childNodes.length > 1) {
|
||||
warning(false, 'Find more than one child node with `children` in ResizeObserver. Please use ResizeObserver.Collection instead.');
|
||||
} else if (childNodes.length === 0) {
|
||||
warning(false, '`children` of ResizeObserver is empty. Nothing is in observe.');
|
||||
}
|
||||
}
|
||||
return childNodes.map((child, index) => {
|
||||
const key = child?.key || `${INTERNAL_PREFIX_KEY}-${index}`;
|
||||
return /*#__PURE__*/React.createElement(SingleObserver, _extends({}, props, {
|
||||
key: key,
|
||||
ref: index === 0 ? ref : undefined
|
||||
}), child);
|
||||
});
|
||||
}
|
||||
const RefResizeObserver = /*#__PURE__*/React.forwardRef(ResizeObserver);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefResizeObserver.displayName = 'ResizeObserver';
|
||||
}
|
||||
RefResizeObserver.Collection = Collection;
|
||||
export default RefResizeObserver;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { OnResize } from '.';
|
||||
export default function useResizeObserver(enabled: boolean, getTarget: HTMLElement | (() => HTMLElement), onDelayResize?: OnResize, onSyncResize?: OnResize): void;
|
||||
Generated
Vendored
+81
@@ -0,0 +1,81 @@
|
||||
import * as React from 'react';
|
||||
import { observe, unobserve } from "./utils/observerUtil";
|
||||
import { useEvent } from '@rc-component/util';
|
||||
export default function useResizeObserver(enabled, getTarget, onDelayResize, onSyncResize) {
|
||||
// ============================= Size =============================
|
||||
const sizeRef = React.useRef({
|
||||
width: -1,
|
||||
height: -1,
|
||||
offsetWidth: -1,
|
||||
offsetHeight: -1
|
||||
});
|
||||
|
||||
// =========================== Observe ============================
|
||||
|
||||
// Handler
|
||||
const onInternalResize = useEvent(target => {
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = target.getBoundingClientRect();
|
||||
const {
|
||||
offsetWidth,
|
||||
offsetHeight
|
||||
} = target;
|
||||
|
||||
/**
|
||||
* Resize observer trigger when content size changed.
|
||||
* In most case we just care about element size,
|
||||
* let's use `boundary` instead of `contentRect` here to avoid shaking.
|
||||
*/
|
||||
const fixedWidth = Math.floor(width);
|
||||
const fixedHeight = Math.floor(height);
|
||||
if (sizeRef.current.width !== fixedWidth || sizeRef.current.height !== fixedHeight || sizeRef.current.offsetWidth !== offsetWidth || sizeRef.current.offsetHeight !== offsetHeight) {
|
||||
const size = {
|
||||
width: fixedWidth,
|
||||
height: fixedHeight,
|
||||
offsetWidth,
|
||||
offsetHeight
|
||||
};
|
||||
sizeRef.current = size;
|
||||
|
||||
// IE is strange, right?
|
||||
const mergedOffsetWidth = offsetWidth === Math.round(width) ? width : offsetWidth;
|
||||
const mergedOffsetHeight = offsetHeight === Math.round(height) ? height : offsetHeight;
|
||||
const sizeInfo = {
|
||||
...size,
|
||||
offsetWidth: mergedOffsetWidth,
|
||||
offsetHeight: mergedOffsetHeight
|
||||
};
|
||||
|
||||
// Call the callback immediately, let the caller decide whether to defer
|
||||
// onResize(sizeInfo, target);
|
||||
onSyncResize?.(sizeInfo, target);
|
||||
|
||||
// defer the callback but not defer to next frame
|
||||
Promise.resolve().then(() => {
|
||||
onDelayResize?.(sizeInfo, target);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Dynamic observe
|
||||
const isFuncTarget = typeof getTarget === 'function';
|
||||
const funcTargetIdRef = React.useRef(0);
|
||||
React.useEffect(() => {
|
||||
const target = isFuncTarget ? getTarget() : getTarget;
|
||||
if (target && enabled) {
|
||||
observe(target, onInternalResize);
|
||||
} else if (enabled && isFuncTarget) {
|
||||
funcTargetIdRef.current += 1;
|
||||
}
|
||||
return () => {
|
||||
if (target) {
|
||||
unobserve(target, onInternalResize);
|
||||
}
|
||||
};
|
||||
}, [enabled,
|
||||
// If function target resolves after a parent render, the bumped ref value
|
||||
// lets the next render re-run this effect without watching the function identity.
|
||||
isFuncTarget ? funcTargetIdRef.current : getTarget]);
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
export type ResizeListener = (element: Element) => void;
|
||||
declare function onResize(entities: ResizeObserverEntry[]): void;
|
||||
export declare const _el: Map<Element, Set<ResizeListener>>;
|
||||
export declare const _rs: typeof onResize;
|
||||
export declare function observe(element: Element, callback: ResizeListener): void;
|
||||
export declare function unobserve(element: Element, callback: ResizeListener): void;
|
||||
export {};
|
||||
Generated
Vendored
+41
@@ -0,0 +1,41 @@
|
||||
// =============================== Const ===============================
|
||||
const elementListeners = new Map();
|
||||
function onResize(entities) {
|
||||
entities.forEach(entity => {
|
||||
const {
|
||||
target
|
||||
} = entity;
|
||||
elementListeners.get(target)?.forEach(listener => listener(target));
|
||||
});
|
||||
}
|
||||
|
||||
// Delay create ResizeObserver since it's not supported in server side
|
||||
let observer;
|
||||
function ensureResizeObserver() {
|
||||
if (!observer) {
|
||||
observer = new ResizeObserver(onResize);
|
||||
}
|
||||
return observer;
|
||||
}
|
||||
|
||||
// Dev env only
|
||||
export const _el = process.env.NODE_ENV !== 'production' ? elementListeners : null; // eslint-disable-line
|
||||
export const _rs = process.env.NODE_ENV !== 'production' ? onResize : null; // eslint-disable-line
|
||||
|
||||
// ============================== Observe ==============================
|
||||
export function observe(element, callback) {
|
||||
if (!elementListeners.has(element)) {
|
||||
elementListeners.set(element, new Set());
|
||||
ensureResizeObserver().observe(element);
|
||||
}
|
||||
elementListeners.get(element).add(callback);
|
||||
}
|
||||
export function unobserve(element, callback) {
|
||||
if (elementListeners.has(element)) {
|
||||
elementListeners.get(element).delete(callback);
|
||||
if (!elementListeners.get(element).size) {
|
||||
ensureResizeObserver().unobserve(element);
|
||||
elementListeners.delete(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
import type { SizeInfo } from '.';
|
||||
type onCollectionResize = (size: SizeInfo, element: HTMLElement, data: any) => void;
|
||||
export declare const CollectionContext: React.Context<onCollectionResize>;
|
||||
export interface ResizeInfo {
|
||||
size: SizeInfo;
|
||||
data: any;
|
||||
element: HTMLElement;
|
||||
}
|
||||
export interface CollectionProps {
|
||||
/** Trigger when some children ResizeObserver changed. Collect by frame render level */
|
||||
onBatchResize?: (resizeInfo: ResizeInfo[]) => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
/**
|
||||
* Collect all the resize event from children ResizeObserver
|
||||
*/
|
||||
export declare function Collection({ children, onBatchResize }: CollectionProps): React.JSX.Element;
|
||||
export {};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Collection = Collection;
|
||||
exports.CollectionContext = void 0;
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (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; }
|
||||
const CollectionContext = exports.CollectionContext = /*#__PURE__*/React.createContext(null);
|
||||
/**
|
||||
* Collect all the resize event from children ResizeObserver
|
||||
*/
|
||||
function Collection({
|
||||
children,
|
||||
onBatchResize
|
||||
}) {
|
||||
const resizeIdRef = React.useRef(0);
|
||||
const resizeInfosRef = React.useRef([]);
|
||||
const onCollectionResize = React.useContext(CollectionContext);
|
||||
const onResize = React.useCallback((size, element, data) => {
|
||||
resizeIdRef.current += 1;
|
||||
const currentId = resizeIdRef.current;
|
||||
resizeInfosRef.current.push({
|
||||
size,
|
||||
element,
|
||||
data
|
||||
});
|
||||
Promise.resolve().then(() => {
|
||||
if (currentId === resizeIdRef.current) {
|
||||
onBatchResize?.(resizeInfosRef.current);
|
||||
resizeInfosRef.current = [];
|
||||
}
|
||||
});
|
||||
|
||||
// Continue bubbling if parent exist
|
||||
onCollectionResize?.(size, element, data);
|
||||
}, [onBatchResize, onCollectionResize]);
|
||||
return /*#__PURE__*/React.createElement(CollectionContext.Provider, {
|
||||
value: onResize
|
||||
}, children);
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import type { ResizeObserverProps } from '..';
|
||||
export interface SingleObserverProps extends ResizeObserverProps {
|
||||
children: React.ReactElement | ((ref: React.RefObject<Element>) => React.ReactElement);
|
||||
}
|
||||
declare const RefSingleObserver: React.ForwardRefExoticComponent<SingleObserverProps & React.RefAttributes<HTMLElement>>;
|
||||
export default RefSingleObserver;
|
||||
Generated
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _findDOMNode = require("@rc-component/util/lib/Dom/findDOMNode");
|
||||
var _ref = require("@rc-component/util/lib/ref");
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _Collection = require("../Collection");
|
||||
var _useResizeObserver = _interopRequireDefault(require("../useResizeObserver"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (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; }
|
||||
function SingleObserver(props, ref) {
|
||||
const {
|
||||
children,
|
||||
disabled,
|
||||
onResize,
|
||||
data
|
||||
} = props;
|
||||
const elementRef = React.useRef(null);
|
||||
const onCollectionResize = React.useContext(_Collection.CollectionContext);
|
||||
|
||||
// =========================== Children ===========================
|
||||
const isRenderProps = typeof children === 'function';
|
||||
const mergedChildren = isRenderProps ? children(elementRef) : children;
|
||||
|
||||
// ============================= Ref ==============================
|
||||
const canRef = !isRenderProps && /*#__PURE__*/React.isValidElement(mergedChildren) && (0, _ref.supportRef)(mergedChildren);
|
||||
const originRef = canRef ? (0, _ref.getNodeRef)(mergedChildren) : null;
|
||||
const mergedRef = (0, _ref.useComposeRef)(originRef, elementRef);
|
||||
const getDomElement = () => {
|
||||
return (0, _findDOMNode.getDOM)(elementRef.current);
|
||||
};
|
||||
React.useImperativeHandle(ref, () => getDomElement());
|
||||
|
||||
// =========================== Observe ============================
|
||||
(0, _useResizeObserver.default)(!disabled, getDomElement, onResize, (sizeInfo, target) => {
|
||||
onCollectionResize?.(sizeInfo, target, data);
|
||||
});
|
||||
|
||||
// ============================ Render ============================
|
||||
return canRef ? /*#__PURE__*/React.cloneElement(mergedChildren, {
|
||||
ref: mergedRef
|
||||
}) : mergedChildren;
|
||||
}
|
||||
const RefSingleObserver = /*#__PURE__*/React.forwardRef(SingleObserver);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefSingleObserver.displayName = 'SingleObserver';
|
||||
}
|
||||
var _default = exports.default = RefSingleObserver;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { Collection } from './Collection';
|
||||
import { _rs } from './utils/observerUtil';
|
||||
export {
|
||||
/** @private Test only for mock trigger resize event */
|
||||
_rs, };
|
||||
export { default as useResizeObserver } from './useResizeObserver';
|
||||
export interface SizeInfo {
|
||||
width: number;
|
||||
height: number;
|
||||
offsetWidth: number;
|
||||
offsetHeight: number;
|
||||
}
|
||||
export type OnResize = (size: SizeInfo, element: HTMLElement) => void;
|
||||
export interface ResizeObserverProps {
|
||||
/** Pass to ResizeObserver.Collection with additional data */
|
||||
data?: any;
|
||||
children: React.ReactNode | ((ref: React.RefObject<any>) => React.ReactElement);
|
||||
disabled?: boolean;
|
||||
/** Trigger if element resized. Will always trigger when first time render. */
|
||||
onResize?: OnResize;
|
||||
}
|
||||
declare const RefResizeObserver: React.ForwardRefExoticComponent<ResizeObserverProps & React.RefAttributes<any>> & {
|
||||
Collection: typeof Collection;
|
||||
};
|
||||
export default RefResizeObserver;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "_rs", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _observerUtil._rs;
|
||||
}
|
||||
});
|
||||
exports.default = void 0;
|
||||
Object.defineProperty(exports, "useResizeObserver", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _useResizeObserver.default;
|
||||
}
|
||||
});
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _toArray = _interopRequireDefault(require("@rc-component/util/lib/Children/toArray"));
|
||||
var _warning = require("@rc-component/util/lib/warning");
|
||||
var _SingleObserver = _interopRequireDefault(require("./SingleObserver"));
|
||||
var _Collection = require("./Collection");
|
||||
var _observerUtil = require("./utils/observerUtil");
|
||||
var _useResizeObserver = _interopRequireDefault(require("./useResizeObserver"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (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; }
|
||||
function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); }
|
||||
const INTERNAL_PREFIX_KEY = 'rc-observer-key';
|
||||
function ResizeObserver(props, ref) {
|
||||
const {
|
||||
children
|
||||
} = props;
|
||||
const childNodes = typeof children === 'function' ? [children] : (0, _toArray.default)(children);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (childNodes.length > 1) {
|
||||
(0, _warning.warning)(false, 'Find more than one child node with `children` in ResizeObserver. Please use ResizeObserver.Collection instead.');
|
||||
} else if (childNodes.length === 0) {
|
||||
(0, _warning.warning)(false, '`children` of ResizeObserver is empty. Nothing is in observe.');
|
||||
}
|
||||
}
|
||||
return childNodes.map((child, index) => {
|
||||
const key = child?.key || `${INTERNAL_PREFIX_KEY}-${index}`;
|
||||
return /*#__PURE__*/React.createElement(_SingleObserver.default, _extends({}, props, {
|
||||
key: key,
|
||||
ref: index === 0 ? ref : undefined
|
||||
}), child);
|
||||
});
|
||||
}
|
||||
const RefResizeObserver = /*#__PURE__*/React.forwardRef(ResizeObserver);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefResizeObserver.displayName = 'ResizeObserver';
|
||||
}
|
||||
RefResizeObserver.Collection = _Collection.Collection;
|
||||
var _default = exports.default = RefResizeObserver;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { OnResize } from '.';
|
||||
export default function useResizeObserver(enabled: boolean, getTarget: HTMLElement | (() => HTMLElement), onDelayResize?: OnResize, onSyncResize?: OnResize): void;
|
||||
Generated
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = useResizeObserver;
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _observerUtil = require("./utils/observerUtil");
|
||||
var _util = require("@rc-component/util");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (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; }
|
||||
function useResizeObserver(enabled, getTarget, onDelayResize, onSyncResize) {
|
||||
// ============================= Size =============================
|
||||
const sizeRef = React.useRef({
|
||||
width: -1,
|
||||
height: -1,
|
||||
offsetWidth: -1,
|
||||
offsetHeight: -1
|
||||
});
|
||||
|
||||
// =========================== Observe ============================
|
||||
|
||||
// Handler
|
||||
const onInternalResize = (0, _util.useEvent)(target => {
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = target.getBoundingClientRect();
|
||||
const {
|
||||
offsetWidth,
|
||||
offsetHeight
|
||||
} = target;
|
||||
|
||||
/**
|
||||
* Resize observer trigger when content size changed.
|
||||
* In most case we just care about element size,
|
||||
* let's use `boundary` instead of `contentRect` here to avoid shaking.
|
||||
*/
|
||||
const fixedWidth = Math.floor(width);
|
||||
const fixedHeight = Math.floor(height);
|
||||
if (sizeRef.current.width !== fixedWidth || sizeRef.current.height !== fixedHeight || sizeRef.current.offsetWidth !== offsetWidth || sizeRef.current.offsetHeight !== offsetHeight) {
|
||||
const size = {
|
||||
width: fixedWidth,
|
||||
height: fixedHeight,
|
||||
offsetWidth,
|
||||
offsetHeight
|
||||
};
|
||||
sizeRef.current = size;
|
||||
|
||||
// IE is strange, right?
|
||||
const mergedOffsetWidth = offsetWidth === Math.round(width) ? width : offsetWidth;
|
||||
const mergedOffsetHeight = offsetHeight === Math.round(height) ? height : offsetHeight;
|
||||
const sizeInfo = {
|
||||
...size,
|
||||
offsetWidth: mergedOffsetWidth,
|
||||
offsetHeight: mergedOffsetHeight
|
||||
};
|
||||
|
||||
// Call the callback immediately, let the caller decide whether to defer
|
||||
// onResize(sizeInfo, target);
|
||||
onSyncResize?.(sizeInfo, target);
|
||||
|
||||
// defer the callback but not defer to next frame
|
||||
Promise.resolve().then(() => {
|
||||
onDelayResize?.(sizeInfo, target);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Dynamic observe
|
||||
const isFuncTarget = typeof getTarget === 'function';
|
||||
const funcTargetIdRef = React.useRef(0);
|
||||
React.useEffect(() => {
|
||||
const target = isFuncTarget ? getTarget() : getTarget;
|
||||
if (target && enabled) {
|
||||
(0, _observerUtil.observe)(target, onInternalResize);
|
||||
} else if (enabled && isFuncTarget) {
|
||||
funcTargetIdRef.current += 1;
|
||||
}
|
||||
return () => {
|
||||
if (target) {
|
||||
(0, _observerUtil.unobserve)(target, onInternalResize);
|
||||
}
|
||||
};
|
||||
}, [enabled,
|
||||
// If function target resolves after a parent render, the bumped ref value
|
||||
// lets the next render re-run this effect without watching the function identity.
|
||||
isFuncTarget ? funcTargetIdRef.current : getTarget]);
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
export type ResizeListener = (element: Element) => void;
|
||||
declare function onResize(entities: ResizeObserverEntry[]): void;
|
||||
export declare const _el: Map<Element, Set<ResizeListener>>;
|
||||
export declare const _rs: typeof onResize;
|
||||
export declare function observe(element: Element, callback: ResizeListener): void;
|
||||
export declare function unobserve(element: Element, callback: ResizeListener): void;
|
||||
export {};
|
||||
Generated
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports._rs = exports._el = void 0;
|
||||
exports.observe = observe;
|
||||
exports.unobserve = unobserve;
|
||||
// =============================== Const ===============================
|
||||
const elementListeners = new Map();
|
||||
function onResize(entities) {
|
||||
entities.forEach(entity => {
|
||||
const {
|
||||
target
|
||||
} = entity;
|
||||
elementListeners.get(target)?.forEach(listener => listener(target));
|
||||
});
|
||||
}
|
||||
|
||||
// Delay create ResizeObserver since it's not supported in server side
|
||||
let observer;
|
||||
function ensureResizeObserver() {
|
||||
if (!observer) {
|
||||
observer = new ResizeObserver(onResize);
|
||||
}
|
||||
return observer;
|
||||
}
|
||||
|
||||
// Dev env only
|
||||
const _el = exports._el = process.env.NODE_ENV !== 'production' ? elementListeners : null; // eslint-disable-line
|
||||
const _rs = exports._rs = process.env.NODE_ENV !== 'production' ? onResize : null; // eslint-disable-line
|
||||
|
||||
// ============================== Observe ==============================
|
||||
function observe(element, callback) {
|
||||
if (!elementListeners.has(element)) {
|
||||
elementListeners.set(element, new Set());
|
||||
ensureResizeObserver().observe(element);
|
||||
}
|
||||
elementListeners.get(element).add(callback);
|
||||
}
|
||||
function unobserve(element, callback) {
|
||||
if (elementListeners.has(element)) {
|
||||
elementListeners.get(element).delete(callback);
|
||||
if (!elementListeners.get(element).size) {
|
||||
ensureResizeObserver().unobserve(element);
|
||||
elementListeners.delete(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "@rc-component/resize-observer",
|
||||
"version": "1.1.2",
|
||||
"description": "Resize observer for React",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-component",
|
||||
"react-resize-observer",
|
||||
"resize-observer"
|
||||
],
|
||||
"main": "./lib/index",
|
||||
"module": "./es/index",
|
||||
"files": [
|
||||
"assets/*.css",
|
||||
"assets/*.less",
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"homepage": "https://react-component.github.io/resize-observer",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:react-component/resize-observer.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "http://github.com/react-component/resize-observer/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "dumi dev",
|
||||
"build": "dumi build",
|
||||
"compile": "father build && lessc assets/index.less assets/index.css",
|
||||
"deploy": "UMI_ENV=gh npm run build && gh-pages -d dist",
|
||||
"gh-pages": "npm run deploy",
|
||||
"prepare": "dumi setup",
|
||||
"prepublishOnly": "npm run compile && rc-np",
|
||||
"postpublish": "npm run gh-pages",
|
||||
"lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md",
|
||||
"prettier": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
|
||||
"test": "rc-test",
|
||||
"coverage": "rc-test --coverage",
|
||||
"now-build": "npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rc-component/util": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rc-component/father-plugin": "^2.0.2",
|
||||
"@rc-component/np": "^1.0.0",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@types/jest": "^29.5.10",
|
||||
"@types/node": "^24.5.2",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@umijs/fabric": "^4.0.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"coveralls": "^3.0.6",
|
||||
"cross-env": "^7.0.2",
|
||||
"dumi": "^2.0.0",
|
||||
"enzyme": "^3.0.0",
|
||||
"enzyme-adapter-react-16": "^1.15.6",
|
||||
"enzyme-to-json": "^3.4.0",
|
||||
"father": "^4.0.0",
|
||||
"gh-pages": "^6.1.0",
|
||||
"glob": "^7.1.6",
|
||||
"less": "^4.1.3",
|
||||
"prettier": "^3.2.5",
|
||||
"pretty-quick": "^4.0.0",
|
||||
"rc-test": "^7.0.15",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"regenerator-runtime": "^0.14.0",
|
||||
"eslint": "8.x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0",
|
||||
"react-dom": ">=16.9.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/minimatch": "5.1.2"
|
||||
},
|
||||
"cnpm": {
|
||||
"mode": "npm"
|
||||
},
|
||||
"tnpm": {
|
||||
"mode": "npm"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user