react-resize-detector
Advanced tools
Comparing version 10.0.1 to 11.0.0-beta.0
@@ -1,2 +0,5 @@ | ||
import {useRef,useState,useMemo,useCallback,useEffect}from'react';import debounce from'lodash/debounce';import throttle from'lodash/throttle';const patchResizeCallback = (resizeCallback, refreshMode, refreshRate, refreshOptions) => { | ||
import*as React from'react';import {useRef,useState,useCallback,useEffect}from'react';import debounce from'lodash/debounce';import throttle from'lodash/throttle';/** | ||
* Wraps the resize callback with a lodash debounce / throttle based on the refresh mode | ||
*/ | ||
const patchResizeCallback = (resizeCallback, refreshMode, refreshRate, refreshOptions) => { | ||
switch (refreshMode) { | ||
@@ -10,10 +13,23 @@ case 'debounce': | ||
} | ||
};function useResizeDetector({ skipOnMount = false, refreshMode, refreshRate = 1000, refreshOptions, handleWidth = true, handleHeight = true, targetRef, observerOptions, onResize } = {}) { | ||
const skipResize = useRef(skipOnMount); | ||
const [size, setSize] = useState({ | ||
width: undefined, | ||
height: undefined | ||
}; | ||
/** | ||
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a | ||
* prop or avoid re-executing effects when passed as a dependency | ||
*/ | ||
const useCallbackRef = | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(callback) => { | ||
const callbackRef = React.useRef(callback); | ||
React.useEffect(() => { | ||
callbackRef.current = callback; | ||
}); | ||
return React.useMemo(() => ((...args) => { var _a; return (_a = callbackRef.current) === null || _a === void 0 ? void 0 : _a.call(callbackRef, ...args); }), []); | ||
}; | ||
/** `useRef` hook doesn't handle conditional rendering or dynamic ref changes. | ||
* This hook creates a proxy that ensures that `refElement` is updated whenever the ref is changed. */ | ||
const useRefProxy = | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(targetRef) => { | ||
// we are going to use this ref to store the last element that was passed to the hook | ||
const [refElement, setRefElement] = useState((targetRef === null || targetRef === void 0 ? void 0 : targetRef.current) || null); | ||
const [refElement, setRefElement] = React.useState((targetRef === null || targetRef === void 0 ? void 0 : targetRef.current) || null); | ||
// if targetRef is passed, we need to update the refElement | ||
@@ -32,3 +48,3 @@ // we have to use setTimeout because ref get assigned after the hook is called | ||
// we call setState inside to trigger rerender | ||
const refProxy = useMemo(() => new Proxy(node => { | ||
const refProxy = React.useMemo(() => new Proxy(node => { | ||
if (node !== refElement) { | ||
@@ -54,14 +70,42 @@ setRefElement(node); | ||
}), [refElement]); | ||
const shouldSetSize = useCallback((prevSize, nextSize) => { | ||
if (prevSize.width === nextSize.width && prevSize.height === nextSize.height) { | ||
// skip if dimensions haven't changed | ||
return false; | ||
} | ||
if ((prevSize.width === nextSize.width && !handleHeight) || | ||
(prevSize.height === nextSize.height && !handleWidth)) { | ||
// process `handleHeight/handleWidth` props | ||
return false; | ||
} | ||
return true; | ||
}, [handleWidth, handleHeight]); | ||
return { refProxy, refElement, setRefElement }; | ||
}; | ||
/** Calculates the dimensions of the element based on the current box model. | ||
* @see https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model | ||
*/ | ||
const getDimensions = (entry, box) => { | ||
// Value Border Padding Inner Content | ||
// --------------------------------------------------- | ||
// 'border-box' Yes Yes Yes | ||
// 'content-box' No No Yes | ||
// undefined No No? Yes | ||
if (box === 'border-box') { | ||
return { | ||
width: entry.borderBoxSize[0].inlineSize, | ||
height: entry.borderBoxSize[0].blockSize | ||
}; | ||
} | ||
if (box === 'content-box') { | ||
return { | ||
width: entry.contentBoxSize[0].inlineSize, | ||
height: entry.contentBoxSize[0].blockSize | ||
}; | ||
} | ||
return { | ||
width: entry.contentRect.width, | ||
height: entry.contentRect.height | ||
}; | ||
};// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function useResizeDetector({ skipOnMount = false, refreshMode, refreshRate = 1000, refreshOptions, handleWidth = true, handleHeight = true, targetRef, observerOptions, onResize } = {}) { | ||
// If `skipOnMount` is enabled, skip the first resize event | ||
const skipResize = useRef(skipOnMount); | ||
// Wrap the `onResize` callback with a ref to avoid re-renders | ||
const onResizeRef = useCallbackRef(onResize); | ||
const [size, setSize] = useState({ | ||
width: undefined, | ||
height: undefined | ||
}); | ||
// Create a proxy ref to handle conditional rendering and dynamic ref changes of the target element | ||
const { refProxy, refElement } = useRefProxy(targetRef); | ||
const { box } = observerOptions || {}; | ||
const resizeCallback = useCallback((entries) => { | ||
@@ -74,11 +118,19 @@ if (!handleWidth && !handleHeight) | ||
} | ||
// Only update the size if one of the observed dimensions has changed | ||
const shouldSetSize = (prevSize, nextSize) => (handleWidth && prevSize.width !== nextSize.width) || (handleHeight && prevSize.height !== nextSize.height); | ||
entries.forEach(entry => { | ||
const { width, height } = (entry === null || entry === void 0 ? void 0 : entry.contentRect) || {}; | ||
const dimensions = getDimensions(entry, box); | ||
setSize(prevSize => { | ||
if (!shouldSetSize(prevSize, { width, height })) | ||
if (!shouldSetSize(prevSize, dimensions)) | ||
return prevSize; | ||
return { width, height }; | ||
onResizeRef === null || onResizeRef === void 0 ? void 0 : onResizeRef({ | ||
width: dimensions.width, | ||
height: dimensions.height, | ||
entry | ||
}); | ||
return dimensions; | ||
}); | ||
}); | ||
}, [handleWidth, handleHeight, skipResize, shouldSetSize]); | ||
}, [handleWidth, handleHeight, skipResize, box]); | ||
// Throttle/Debounce the resize event if refreshMode is configured | ||
const resizeHandler = useCallback(patchResizeCallback(resizeCallback, refreshMode, refreshRate, refreshOptions), [ | ||
@@ -90,3 +142,3 @@ resizeCallback, | ||
]); | ||
// on refElement change | ||
// Attach ResizeObserver to the element | ||
useEffect(() => { | ||
@@ -98,7 +150,12 @@ let resizeObserver; | ||
} | ||
else { | ||
if (size.width || size.height) { | ||
setSize({ width: undefined, height: undefined }); | ||
} | ||
// If refElement is not available, reset the size | ||
else if (size.width || size.height) { | ||
onResizeRef === null || onResizeRef === void 0 ? void 0 : onResizeRef({ | ||
width: null, | ||
height: null, | ||
entry: null | ||
}); | ||
setSize({ width: undefined, height: undefined }); | ||
} | ||
// Disconnect the ResizeObserver when the component is unmounted | ||
return () => { | ||
@@ -110,6 +167,3 @@ var _a, _b, _c; | ||
}, [resizeHandler, refElement]); | ||
useEffect(() => { | ||
onResize === null || onResize === void 0 ? void 0 : onResize(size.width, size.height); | ||
}, [size]); | ||
return Object.assign({ ref: refProxy }, size); | ||
}export{useResizeDetector};//# sourceMappingURL=index.esm.js.map |
@@ -1,2 +0,5 @@ | ||
'use strict';var react=require('react'),debounce=require('lodash/debounce'),throttle=require('lodash/throttle');const patchResizeCallback = (resizeCallback, refreshMode, refreshRate, refreshOptions) => { | ||
'use strict';var React=require('react'),debounce=require('lodash/debounce'),throttle=require('lodash/throttle');function _interopNamespaceDefault(e){var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var React__namespace=/*#__PURE__*/_interopNamespaceDefault(React);/** | ||
* Wraps the resize callback with a lodash debounce / throttle based on the refresh mode | ||
*/ | ||
const patchResizeCallback = (resizeCallback, refreshMode, refreshRate, refreshOptions) => { | ||
switch (refreshMode) { | ||
@@ -10,10 +13,23 @@ case 'debounce': | ||
} | ||
};function useResizeDetector({ skipOnMount = false, refreshMode, refreshRate = 1000, refreshOptions, handleWidth = true, handleHeight = true, targetRef, observerOptions, onResize } = {}) { | ||
const skipResize = react.useRef(skipOnMount); | ||
const [size, setSize] = react.useState({ | ||
width: undefined, | ||
height: undefined | ||
}; | ||
/** | ||
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a | ||
* prop or avoid re-executing effects when passed as a dependency | ||
*/ | ||
const useCallbackRef = | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(callback) => { | ||
const callbackRef = React__namespace.useRef(callback); | ||
React__namespace.useEffect(() => { | ||
callbackRef.current = callback; | ||
}); | ||
return React__namespace.useMemo(() => ((...args) => { var _a; return (_a = callbackRef.current) === null || _a === void 0 ? void 0 : _a.call(callbackRef, ...args); }), []); | ||
}; | ||
/** `useRef` hook doesn't handle conditional rendering or dynamic ref changes. | ||
* This hook creates a proxy that ensures that `refElement` is updated whenever the ref is changed. */ | ||
const useRefProxy = | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(targetRef) => { | ||
// we are going to use this ref to store the last element that was passed to the hook | ||
const [refElement, setRefElement] = react.useState((targetRef === null || targetRef === void 0 ? void 0 : targetRef.current) || null); | ||
const [refElement, setRefElement] = React__namespace.useState((targetRef === null || targetRef === void 0 ? void 0 : targetRef.current) || null); | ||
// if targetRef is passed, we need to update the refElement | ||
@@ -32,3 +48,3 @@ // we have to use setTimeout because ref get assigned after the hook is called | ||
// we call setState inside to trigger rerender | ||
const refProxy = react.useMemo(() => new Proxy(node => { | ||
const refProxy = React__namespace.useMemo(() => new Proxy(node => { | ||
if (node !== refElement) { | ||
@@ -54,15 +70,43 @@ setRefElement(node); | ||
}), [refElement]); | ||
const shouldSetSize = react.useCallback((prevSize, nextSize) => { | ||
if (prevSize.width === nextSize.width && prevSize.height === nextSize.height) { | ||
// skip if dimensions haven't changed | ||
return false; | ||
} | ||
if ((prevSize.width === nextSize.width && !handleHeight) || | ||
(prevSize.height === nextSize.height && !handleWidth)) { | ||
// process `handleHeight/handleWidth` props | ||
return false; | ||
} | ||
return true; | ||
}, [handleWidth, handleHeight]); | ||
const resizeCallback = react.useCallback((entries) => { | ||
return { refProxy, refElement, setRefElement }; | ||
}; | ||
/** Calculates the dimensions of the element based on the current box model. | ||
* @see https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model | ||
*/ | ||
const getDimensions = (entry, box) => { | ||
// Value Border Padding Inner Content | ||
// --------------------------------------------------- | ||
// 'border-box' Yes Yes Yes | ||
// 'content-box' No No Yes | ||
// undefined No No? Yes | ||
if (box === 'border-box') { | ||
return { | ||
width: entry.borderBoxSize[0].inlineSize, | ||
height: entry.borderBoxSize[0].blockSize | ||
}; | ||
} | ||
if (box === 'content-box') { | ||
return { | ||
width: entry.contentBoxSize[0].inlineSize, | ||
height: entry.contentBoxSize[0].blockSize | ||
}; | ||
} | ||
return { | ||
width: entry.contentRect.width, | ||
height: entry.contentRect.height | ||
}; | ||
};// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
function useResizeDetector({ skipOnMount = false, refreshMode, refreshRate = 1000, refreshOptions, handleWidth = true, handleHeight = true, targetRef, observerOptions, onResize } = {}) { | ||
// If `skipOnMount` is enabled, skip the first resize event | ||
const skipResize = React.useRef(skipOnMount); | ||
// Wrap the `onResize` callback with a ref to avoid re-renders | ||
const onResizeRef = useCallbackRef(onResize); | ||
const [size, setSize] = React.useState({ | ||
width: undefined, | ||
height: undefined | ||
}); | ||
// Create a proxy ref to handle conditional rendering and dynamic ref changes of the target element | ||
const { refProxy, refElement } = useRefProxy(targetRef); | ||
const { box } = observerOptions || {}; | ||
const resizeCallback = React.useCallback((entries) => { | ||
if (!handleWidth && !handleHeight) | ||
@@ -74,12 +118,20 @@ return; | ||
} | ||
// Only update the size if one of the observed dimensions has changed | ||
const shouldSetSize = (prevSize, nextSize) => (handleWidth && prevSize.width !== nextSize.width) || (handleHeight && prevSize.height !== nextSize.height); | ||
entries.forEach(entry => { | ||
const { width, height } = (entry === null || entry === void 0 ? void 0 : entry.contentRect) || {}; | ||
const dimensions = getDimensions(entry, box); | ||
setSize(prevSize => { | ||
if (!shouldSetSize(prevSize, { width, height })) | ||
if (!shouldSetSize(prevSize, dimensions)) | ||
return prevSize; | ||
return { width, height }; | ||
onResizeRef === null || onResizeRef === void 0 ? void 0 : onResizeRef({ | ||
width: dimensions.width, | ||
height: dimensions.height, | ||
entry | ||
}); | ||
return dimensions; | ||
}); | ||
}); | ||
}, [handleWidth, handleHeight, skipResize, shouldSetSize]); | ||
const resizeHandler = react.useCallback(patchResizeCallback(resizeCallback, refreshMode, refreshRate, refreshOptions), [ | ||
}, [handleWidth, handleHeight, skipResize, box]); | ||
// Throttle/Debounce the resize event if refreshMode is configured | ||
const resizeHandler = React.useCallback(patchResizeCallback(resizeCallback, refreshMode, refreshRate, refreshOptions), [ | ||
resizeCallback, | ||
@@ -90,4 +142,4 @@ refreshMode, | ||
]); | ||
// on refElement change | ||
react.useEffect(() => { | ||
// Attach ResizeObserver to the element | ||
React.useEffect(() => { | ||
let resizeObserver; | ||
@@ -98,7 +150,12 @@ if (refElement) { | ||
} | ||
else { | ||
if (size.width || size.height) { | ||
setSize({ width: undefined, height: undefined }); | ||
} | ||
// If refElement is not available, reset the size | ||
else if (size.width || size.height) { | ||
onResizeRef === null || onResizeRef === void 0 ? void 0 : onResizeRef({ | ||
width: null, | ||
height: null, | ||
entry: null | ||
}); | ||
setSize({ width: undefined, height: undefined }); | ||
} | ||
// Disconnect the ResizeObserver when the component is unmounted | ||
return () => { | ||
@@ -110,6 +167,3 @@ var _a, _b, _c; | ||
}, [resizeHandler, refElement]); | ||
react.useEffect(() => { | ||
onResize === null || onResize === void 0 ? void 0 : onResize(size.width, size.height); | ||
}, [size]); | ||
return Object.assign({ ref: refProxy }, size); | ||
}exports.useResizeDetector=useResizeDetector;//# sourceMappingURL=index.js.map |
import useResizeDetector from './useResizeDetector'; | ||
export { useResizeDetector }; | ||
export type { UseResizeDetectorReturn, useResizeDetectorProps } from './types'; | ||
export type { UseResizeDetectorReturn, useResizeDetectorProps, OnResizeCallback, ResizePayload, ResfreshModeType, ResfreshOptionsType, Dimensions } from './types'; |
import type { MutableRefObject } from 'react'; | ||
export type ReactResizeDetectorDimensions = { | ||
export type Dimensions = { | ||
height?: number; | ||
width?: number; | ||
}; | ||
/** If element is mounted, returns its dimensions and `ResizeObserverEntry` | ||
* If element is unmounted, returns null */ | ||
export type ResizePayload = { | ||
width: number; | ||
height: number; | ||
entry: ResizeObserverEntry; | ||
} | { | ||
width: null; | ||
height: null; | ||
entry: null; | ||
}; | ||
export type ResfreshModeType = 'throttle' | 'debounce'; | ||
@@ -11,6 +22,7 @@ export type ResfreshOptionsType = { | ||
}; | ||
export type OnResizeCallback = (width?: number, height?: number) => void; | ||
export type OnResizeCallback = (payload: ResizePayload) => void; | ||
export type Props = { | ||
/** | ||
* Function that will be invoked with observable element's width and height. | ||
* Function that will be invoked with observable element's width, height and ResizeObserverEntry. | ||
* If element is unmounted, width and height will be null. | ||
* Default: undefined | ||
@@ -62,3 +74,3 @@ */ | ||
}; | ||
export interface UseResizeDetectorReturn<T> extends ReactResizeDetectorDimensions { | ||
export interface UseResizeDetectorReturn<T> extends Dimensions { | ||
ref: OnRefChangeType<T>; | ||
@@ -65,0 +77,0 @@ } |
@@ -0,4 +1,27 @@ | ||
import * as React from 'react'; | ||
import type { DebouncedFunc } from 'lodash'; | ||
import { Props } from './types'; | ||
import { OnRefChangeType, Props } from './types'; | ||
export type PatchedResizeObserverCallback = DebouncedFunc<ResizeObserverCallback> | ResizeObserverCallback; | ||
/** | ||
* Wraps the resize callback with a lodash debounce / throttle based on the refresh mode | ||
*/ | ||
export declare const patchResizeCallback: (resizeCallback: ResizeObserverCallback, refreshMode: Props['refreshMode'], refreshRate: Props['refreshRate'], refreshOptions: Props['refreshOptions']) => PatchedResizeObserverCallback; | ||
/** | ||
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a | ||
* prop or avoid re-executing effects when passed as a dependency | ||
*/ | ||
export declare const useCallbackRef: <T extends (...args: any[]) => any>(callback: T | undefined) => T; | ||
/** `useRef` hook doesn't handle conditional rendering or dynamic ref changes. | ||
* This hook creates a proxy that ensures that `refElement` is updated whenever the ref is changed. */ | ||
export declare const useRefProxy: <T extends HTMLElement = any>(targetRef: React.MutableRefObject<T | null> | undefined) => { | ||
refProxy: OnRefChangeType<T>; | ||
refElement: T | null; | ||
setRefElement: React.Dispatch<React.SetStateAction<T | null>>; | ||
}; | ||
/** Calculates the dimensions of the element based on the current box model. | ||
* @see https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model | ||
*/ | ||
export declare const getDimensions: (entry: ResizeObserverEntry, box: ResizeObserverBoxOptions | undefined) => { | ||
width: number; | ||
height: number; | ||
}; |
{ | ||
"name": "react-resize-detector", | ||
"version": "10.0.1", | ||
"version": "11.0.0-beta.0", | ||
"description": "React resize detector", | ||
@@ -19,5 +19,4 @@ "main": "build/index.js", | ||
"prettier": "prettier --write .", | ||
"lint": "eslint .", | ||
"lint-fix": "eslint --fix", | ||
"fix": "npm run prettier && npm run lint-fix", | ||
"lint": "eslint . --fix", | ||
"fix": "npm run prettier && npm run lint", | ||
"prerelease": "npm version prerelease --preid=rc", | ||
@@ -30,19 +29,17 @@ "prepublishOnly": "npm run build" | ||
"devDependencies": { | ||
"@rollup/plugin-commonjs": "^25.0.7", | ||
"@rollup/plugin-commonjs": "^25.0.8", | ||
"@rollup/plugin-node-resolve": "^15.2.3", | ||
"@rollup/plugin-typescript": "^11.1.6", | ||
"@types/lodash": "^4.14.202", | ||
"@types/react": "^18.2.47", | ||
"@types/react-dom": "^18.2.18", | ||
"@typescript-eslint/eslint-plugin": "^6.18.1", | ||
"@typescript-eslint/parser": "^6.18.1", | ||
"eslint": "^8.56.0", | ||
"@types/lodash": "^4.17.4", | ||
"@types/react": "^18.3.3", | ||
"@types/react-dom": "^18.3.0", | ||
"@typescript-eslint/eslint-plugin": "^7.11.0", | ||
"@typescript-eslint/parser": "^7.11.0", | ||
"eslint": "^8.57.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-import": "^2.29.1", | ||
"eslint-plugin-react": "^7.33.2", | ||
"prettier": "^3.1.1", | ||
"rollup": "^4.9.5", | ||
"rollup-plugin-node-externals": "^6.1.2", | ||
"tslib": "^2.6.2", | ||
"typescript": "^5.3.3" | ||
"eslint-plugin-react": "^7.34.2", | ||
"prettier": "^3.2.5", | ||
"rollup": "^4.18.0", | ||
"rollup-plugin-node-externals": "^7.1.2", | ||
"typescript": "^5.4.5" | ||
}, | ||
@@ -57,5 +54,2 @@ "peerDependencies": { | ||
}, | ||
"directories": { | ||
"example": "example" | ||
}, | ||
"homepage": "https://github.com/maslianok/react-resize-detector", | ||
@@ -62,0 +56,0 @@ "keywords": [ |
# Handle element resizes like it's 2024! | ||
<img src="https://img.shields.io/npm/dm/react-resize-detector?style=flat-square"> <img src="https://badgen.net/bundlephobia/minzip/react-resize-detector?style=flat-square"> <img src="https://badgen.net/bundlephobia/tree-shaking/react-resize-detector?style=flat-square"> | ||
<img src="https://img.shields.io/npm/v/react-resize-detector?style=flat-square" /> <img src="https://img.shields.io/npm/l/react-resize-detector?style=flat-square" /> <img src="https://img.shields.io/npm/dm/react-resize-detector?style=flat-square" /> <img src="https://img.shields.io/bundlejs/size/react-resize-detector?style=flat-square" /> | ||
@@ -13,7 +13,7 @@ #### [Live demo](http://maslianok.github.io/react-resize-detector/) | ||
🐠 Used by <a href="https://github.com/maslianok/react-resize-detector/network/dependents" target="__blank">160k repositories</a> | ||
🐠 Used by <a href="https://github.com/maslianok/react-resize-detector/network/dependents" target="__blank">170k repositories</a> | ||
🦄 Produces <a href="https://npmtrends.com/react-resize-detector" target="__blank">100 million downloads annually</a> | ||
No `window.resize` listeners! No timeouts! | ||
No `window.resize` listeners! No timeouts! | ||
@@ -113,3 +113,3 @@ ## Is it necessary for you to use this library? | ||
| --------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | ||
| onResize | Func | Function that will be invoked with `width` and `height` arguments | `undefined` | | ||
| onResize | Func | Function that will be invoked with `width`, `height` and ResizeObserver `entry` arguments | `undefined` | | ||
| handleWidth | Bool | Trigger `onResize` on width change | `true` | | ||
@@ -116,0 +116,0 @@ | handleHeight | Bool | Trigger `onResize` on height change | `true` | |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
55362
15
436
11
1