@primer/components 31.2.0-rc.50908ce9 → 31.2.0-rc.6afeaa37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/browser.esm.js +622 -620
- package/dist/browser.esm.js.map +1 -1
- package/dist/browser.umd.js +163 -161
- package/dist/browser.umd.js.map +1 -1
- package/docs/content/ActionList2.mdx +354 -0
- package/docs/content/getting-started.md +1 -1
- package/docs/src/@primer/gatsby-theme-doctocat/components/hero.js +1 -3
- package/docs/src/@primer/gatsby-theme-doctocat/components/live-preview-wrapper.js +1 -1
- package/docs/src/@primer/gatsby-theme-doctocat/live-code-scope.js +17 -0
- package/lib/ActionList2/Description.d.ts +12 -0
- package/lib/ActionList2/Description.js +53 -0
- package/lib/ActionList2/Divider.d.ts +5 -0
- package/lib/ActionList2/Divider.js +35 -0
- package/lib/ActionList2/Group.d.ts +11 -0
- package/lib/ActionList2/Group.js +57 -0
- package/lib/ActionList2/Header.d.ts +26 -0
- package/lib/ActionList2/Header.js +55 -0
- package/lib/ActionList2/Item.d.ts +63 -0
- package/lib/ActionList2/Item.js +244 -0
- package/lib/ActionList2/LinkItem.d.ts +17 -0
- package/lib/ActionList2/LinkItem.js +57 -0
- package/lib/ActionList2/List.d.ts +26 -0
- package/lib/ActionList2/List.js +59 -0
- package/lib/ActionList2/Selection.d.ts +5 -0
- package/lib/ActionList2/Selection.js +70 -0
- package/lib/ActionList2/Visuals.d.ts +9 -0
- package/lib/ActionList2/Visuals.js +90 -0
- package/lib/ActionList2/index.d.ts +36 -0
- package/lib/ActionList2/index.js +47 -0
- package/lib/Overlay.js +3 -1
- package/lib/Portal/Portal.js +3 -2
- package/lib/_TextInputWrapper.js +2 -2
- package/lib/__tests__/ActionList2.test.d.ts +1 -0
- package/lib/__tests__/ActionList2.test.js +53 -0
- package/lib/__tests__/AnchoredOverlay.test.js +4 -2
- package/lib/__tests__/TextInputWithTokens.test.js +1 -10
- package/lib/__tests__/utils/createSlots.test.d.ts +1 -0
- package/lib/__tests__/utils/createSlots.test.js +75 -0
- package/lib/hooks/useAnchoredPosition.js +3 -2
- package/lib/hooks/useCombinedRefs.d.ts +2 -2
- package/lib/hooks/useCombinedRefs.js +4 -6
- package/lib/hooks/useResizeObserver.js +2 -2
- package/lib/stories/ActionList2.stories.js +907 -0
- package/lib/stories/TextInput.stories.js +144 -0
- package/lib/sx.d.ts +2 -0
- package/lib/sx.js +8 -0
- package/lib/theme-preval.js +81 -2
- package/lib/unreleased.d.ts +7 -0
- package/lib/unreleased.js +18 -0
- package/lib/utils/create-slots.d.ts +17 -0
- package/lib/utils/create-slots.js +105 -0
- package/lib/utils/testing.d.ts +14 -1
- package/lib/utils/use-force-update.d.ts +1 -0
- package/lib/utils/use-force-update.js +19 -0
- package/lib/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/lib/utils/useIsomorphicLayoutEffect.js +12 -0
- package/lib-esm/ActionList2/Description.d.ts +12 -0
- package/lib-esm/ActionList2/Description.js +37 -0
- package/lib-esm/ActionList2/Divider.d.ts +5 -0
- package/lib-esm/ActionList2/Divider.js +23 -0
- package/lib-esm/ActionList2/Group.d.ts +11 -0
- package/lib-esm/ActionList2/Group.js +40 -0
- package/lib-esm/ActionList2/Header.d.ts +26 -0
- package/lib-esm/ActionList2/Header.js +44 -0
- package/lib-esm/ActionList2/Item.d.ts +63 -0
- package/lib-esm/ActionList2/Item.js +210 -0
- package/lib-esm/ActionList2/LinkItem.d.ts +17 -0
- package/lib-esm/ActionList2/LinkItem.js +43 -0
- package/lib-esm/ActionList2/List.d.ts +26 -0
- package/lib-esm/ActionList2/List.js +37 -0
- package/lib-esm/ActionList2/Selection.d.ts +5 -0
- package/lib-esm/ActionList2/Selection.js +52 -0
- package/lib-esm/ActionList2/Visuals.d.ts +9 -0
- package/lib-esm/ActionList2/Visuals.js +68 -0
- package/lib-esm/ActionList2/index.d.ts +36 -0
- package/lib-esm/ActionList2/index.js +33 -0
- package/lib-esm/Overlay.js +2 -1
- package/lib-esm/Portal/Portal.js +2 -1
- package/lib-esm/_TextInputWrapper.js +2 -2
- package/lib-esm/__tests__/ActionList2.test.d.ts +1 -0
- package/lib-esm/__tests__/ActionList2.test.js +41 -0
- package/lib-esm/__tests__/AnchoredOverlay.test.js +4 -2
- package/lib-esm/__tests__/TextInputWithTokens.test.js +1 -10
- package/lib-esm/__tests__/utils/createSlots.test.d.ts +1 -0
- package/lib-esm/__tests__/utils/createSlots.test.js +67 -0
- package/lib-esm/hooks/useAnchoredPosition.js +2 -1
- package/lib-esm/hooks/useCombinedRefs.d.ts +2 -2
- package/lib-esm/hooks/useCombinedRefs.js +3 -2
- package/lib-esm/hooks/useResizeObserver.js +2 -2
- package/lib-esm/stories/ActionList2.stories.js +796 -0
- package/lib-esm/stories/TextInput.stories.js +117 -0
- package/lib-esm/sx.d.ts +2 -0
- package/lib-esm/sx.js +3 -1
- package/lib-esm/theme-preval.js +81 -2
- package/lib-esm/unreleased.d.ts +7 -0
- package/lib-esm/unreleased.js +8 -0
- package/lib-esm/utils/create-slots.d.ts +17 -0
- package/lib-esm/utils/create-slots.js +84 -0
- package/lib-esm/utils/testing.d.ts +14 -1
- package/lib-esm/utils/use-force-update.d.ts +1 -0
- package/lib-esm/utils/use-force-update.js +6 -0
- package/lib-esm/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/lib-esm/utils/useIsomorphicLayoutEffect.js +3 -0
- package/migrating.md +1 -1
- package/package-lock.json +146 -7
- package/package.json +5 -3
- package/src/ActionList2/Description.tsx +49 -0
- package/src/ActionList2/Divider.tsx +24 -0
- package/src/ActionList2/Group.tsx +34 -0
- package/src/ActionList2/Header.tsx +58 -0
- package/src/ActionList2/Item.tsx +245 -0
- package/src/ActionList2/LinkItem.tsx +49 -0
- package/src/ActionList2/List.tsx +55 -0
- package/src/ActionList2/Selection.tsx +40 -0
- package/src/ActionList2/Visuals.tsx +76 -0
- package/src/ActionList2/index.ts +39 -0
- package/src/Overlay.tsx +2 -1
- package/src/Portal/Portal.tsx +2 -1
- package/src/_TextInputWrapper.tsx +7 -0
- package/src/__tests__/ActionList2.test.tsx +47 -0
- package/src/__tests__/AnchoredOverlay.test.tsx +2 -2
- package/src/__tests__/TextInputWithTokens.test.tsx +0 -10
- package/src/__tests__/__snapshots__/ActionList2.test.tsx.snap +14 -0
- package/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +35 -135
- package/src/__tests__/utils/__snapshots__/createSlots.test.tsx.snap +55 -0
- package/src/__tests__/utils/createSlots.test.tsx +74 -0
- package/src/hooks/useAnchoredPosition.ts +2 -1
- package/src/hooks/useCombinedRefs.ts +3 -3
- package/src/hooks/useResizeObserver.ts +2 -2
- package/src/stories/ActionList2.stories.tsx +1288 -0
- package/src/stories/TextInput.stories.tsx +113 -0
- package/src/sx.ts +3 -0
- package/src/theme-preval.js +1 -0
- package/src/unreleased.ts +9 -0
- package/src/utils/create-slots.tsx +96 -0
- package/src/utils/use-force-update.ts +7 -0
- package/src/utils/useIsomorphicLayoutEffect.ts +10 -0
- package/stats.html +1 -1
package/lib-esm/Overlay.js
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
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); }
|
2
2
|
|
3
3
|
import styled from 'styled-components';
|
4
|
-
import React, { useEffect,
|
4
|
+
import React, { useEffect, useRef } from 'react';
|
5
5
|
import { get, COMMON } from './constants';
|
6
|
+
import useLayoutEffect from './utils/useIsomorphicLayoutEffect';
|
6
7
|
import { useOverlay } from './hooks';
|
7
8
|
import Portal from './Portal';
|
8
9
|
import sx from './sx';
|
package/lib-esm/Portal/Portal.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { createPortal } from 'react-dom';
|
3
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect';
|
3
4
|
const PRIMER_PORTAL_ROOT_ID = '__primerPortalRoot__';
|
4
5
|
const DEFAULT_PORTAL_CONTAINER_NAME = '__default__';
|
5
6
|
const portalRootRegistry = {};
|
@@ -57,7 +58,7 @@ export const Portal = ({
|
|
57
58
|
hostElement.style.position = 'relative';
|
58
59
|
hostElement.style.zIndex = '1';
|
59
60
|
const elementRef = React.useRef(hostElement);
|
60
|
-
|
61
|
+
useLayoutEffect(() => {
|
61
62
|
let containerName = _containerName;
|
62
63
|
|
63
64
|
if (containerName === undefined) {
|
@@ -21,11 +21,11 @@ const sizeVariants = variant({
|
|
21
21
|
const TextInputWrapper = styled.span.withConfig({
|
22
22
|
displayName: "_TextInputWrapper__TextInputWrapper",
|
23
23
|
componentId: "sc-5vfcis-0"
|
24
|
-
})(["display:inline-flex;align-items:stretch;min-height:34px;font-size:", ";line-height:20px;color:", ";vertical-align:middle;background-repeat:no-repeat;background-position:right 8px center;border:1px solid ", ";border-radius:", ";outline:none;box-shadow:", ";cursor:text;", " .TextInput-icon{align-self:center;color:", ";margin:0 ", ";flex-shrink:0;}&:focus-within{border-color:", ";box-shadow:", ";}", " ", " ", " @media (min-width:", "){font-size:", ";}", " ", " ", " ", " ", ";"], get('fontSizes.1'), get('colors.fg.default'), get('colors.border.default'), get('radii.2'), get('shadows.primer.shadow.inset'), props => {
|
24
|
+
})(["display:inline-flex;align-items:stretch;min-height:34px;font-size:", ";line-height:20px;color:", ";vertical-align:middle;background-repeat:no-repeat;background-position:right 8px center;border:1px solid ", ";border-radius:", ";outline:none;box-shadow:", ";cursor:text;", " .TextInput-icon{align-self:center;color:", ";margin:0 ", ";flex-shrink:0;}&:focus-within{border-color:", ";box-shadow:", ";}", " ", " ", " ", " @media (min-width:", "){font-size:", ";}", " ", " ", " ", " ", ";"], get('fontSizes.1'), get('colors.fg.default'), get('colors.border.default'), get('radii.2'), get('shadows.primer.shadow.inset'), props => {
|
25
25
|
if (props.hasIcon) {
|
26
26
|
return css(["padding:0;"]);
|
27
27
|
} else {
|
28
28
|
return css(["padding:6px 12px;"]);
|
29
29
|
}
|
30
|
-
}, get('colors.fg.muted'), get('space.2'), get('colors.accent.emphasis'), get('shadows.primer.shadow.focus'), props => props.contrast && css(["background-color:", ";"], get('colors.canvas.inset')), props => props.disabled && css(["color:", ";background-color:", ";border-color:", ";"], get('colors.primer.fg.disabled'), get('colors.input.disabledBg'), get('colors.border.default')), props => props.block && css(["display:block;width:100%;"]), get('breakpoints.1'), get('fontSizes.1'), width, minWidth, maxWidth, sizeVariants, sx);
|
30
|
+
}, get('colors.fg.muted'), get('space.2'), get('colors.accent.emphasis'), get('shadows.primer.shadow.focus'), props => props.contrast && css(["background-color:", ";"], get('colors.canvas.inset')), props => props.disabled && css(["color:", ";background-color:", ";border-color:", ";"], get('colors.primer.fg.disabled'), get('colors.input.disabledBg'), get('colors.border.default')), props => props.block && css(["display:block;width:100%;"]), props => props.block && props.hasIcon && css(["display:flex;"]), get('breakpoints.1'), get('fontSizes.1'), width, minWidth, maxWidth, sizeVariants, sx);
|
31
31
|
export default TextInputWrapper;
|
@@ -0,0 +1 @@
|
|
1
|
+
import 'babel-polyfill';
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { cleanup, render as HTMLRender } from '@testing-library/react';
|
2
|
+
import 'babel-polyfill';
|
3
|
+
import { axe, toHaveNoViolations } from 'jest-axe';
|
4
|
+
import React from 'react';
|
5
|
+
import theme from '../theme';
|
6
|
+
import { ActionList } from '../ActionList2';
|
7
|
+
import { behavesAsComponent, checkExports } from '../utils/testing';
|
8
|
+
import { BaseStyles, ThemeProvider, SSRProvider } from '..';
|
9
|
+
expect.extend(toHaveNoViolations);
|
10
|
+
|
11
|
+
function SimpleActionList() {
|
12
|
+
return /*#__PURE__*/React.createElement(ThemeProvider, {
|
13
|
+
theme: theme
|
14
|
+
}, /*#__PURE__*/React.createElement(SSRProvider, null, /*#__PURE__*/React.createElement(BaseStyles, null, /*#__PURE__*/React.createElement(ActionList, null, /*#__PURE__*/React.createElement(ActionList.Item, null, "New file"), /*#__PURE__*/React.createElement(ActionList.Divider, null), /*#__PURE__*/React.createElement(ActionList.Item, null, "Copy link"), /*#__PURE__*/React.createElement(ActionList.Item, null, "Edit file"), /*#__PURE__*/React.createElement(ActionList.Item, {
|
15
|
+
variant: "danger"
|
16
|
+
}, "Delete file")))));
|
17
|
+
}
|
18
|
+
|
19
|
+
SimpleActionList.displayName = "SimpleActionList";
|
20
|
+
describe('ActionList', () => {
|
21
|
+
behavesAsComponent({
|
22
|
+
Component: ActionList,
|
23
|
+
options: {
|
24
|
+
skipAs: true,
|
25
|
+
skipSx: true
|
26
|
+
},
|
27
|
+
toRender: () => /*#__PURE__*/React.createElement(ActionList, null)
|
28
|
+
});
|
29
|
+
checkExports('ActionList2', {
|
30
|
+
default: undefined,
|
31
|
+
ActionList
|
32
|
+
});
|
33
|
+
it('should have no axe violations', async () => {
|
34
|
+
const {
|
35
|
+
container
|
36
|
+
} = HTMLRender( /*#__PURE__*/React.createElement(SimpleActionList, null));
|
37
|
+
const results = await axe(container);
|
38
|
+
expect(results).toHaveNoViolations();
|
39
|
+
cleanup();
|
40
|
+
});
|
41
|
+
});
|
@@ -126,9 +126,11 @@ describe('AnchoredOverlay', () => {
|
|
126
126
|
expect(mockCloseCallback).toHaveBeenCalledWith('escape');
|
127
127
|
});
|
128
128
|
it('should render consistently when open', () => {
|
129
|
-
const
|
129
|
+
const {
|
130
|
+
container
|
131
|
+
} = HTMLRender( /*#__PURE__*/React.createElement(AnchoredOverlayTestComponent, {
|
130
132
|
initiallyOpen: true
|
131
133
|
}));
|
132
|
-
expect(
|
134
|
+
expect(container).toMatchSnapshot();
|
133
135
|
});
|
134
136
|
});
|
@@ -46,16 +46,7 @@ const LabelledTextInputWithTokens = ({
|
|
46
46
|
tokens: tokens,
|
47
47
|
onTokenRemove: onTokenRemove,
|
48
48
|
id: "tokenInput"
|
49
|
-
}, rest)));
|
50
|
-
// it('should have no axe violations', async () => {
|
51
|
-
// const onRemoveMock = jest.fn()
|
52
|
-
// const {container} = HTMLRender(<LabelledTextInputWithTokens tokens={mockTokens} onTokenRemove={onRemoveMock} />)
|
53
|
-
// const results = await axe(container)
|
54
|
-
// expect(results).toHaveNoViolations()
|
55
|
-
// cleanup()
|
56
|
-
// })
|
57
|
-
// })
|
58
|
-
|
49
|
+
}, rest)));
|
59
50
|
|
60
51
|
jest.useFakeTimers();
|
61
52
|
describe('TextInputWithTokens', () => {
|
@@ -0,0 +1 @@
|
|
1
|
+
import 'babel-polyfill';
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import 'babel-polyfill';
|
3
|
+
import { render, waitFor } from '@testing-library/react';
|
4
|
+
import createSlots from '../../utils/create-slots'; // setup a component with slots
|
5
|
+
|
6
|
+
const {
|
7
|
+
Slots,
|
8
|
+
Slot
|
9
|
+
} = createSlots(['One', 'Two', 'Three']);
|
10
|
+
|
11
|
+
const ComponentWithSlots = ({
|
12
|
+
salutation,
|
13
|
+
children
|
14
|
+
}) => {
|
15
|
+
return /*#__PURE__*/React.createElement(Slots, {
|
16
|
+
context: {
|
17
|
+
salutation
|
18
|
+
}
|
19
|
+
}, slots => /*#__PURE__*/React.createElement("div", null, slots.One, /*#__PURE__*/React.createElement("span", null, children, " ", slots.Two, " ", slots.Three)));
|
20
|
+
};
|
21
|
+
|
22
|
+
ComponentWithSlots.displayName = "ComponentWithSlots";
|
23
|
+
|
24
|
+
const SlotItem1 = ({
|
25
|
+
children
|
26
|
+
}) => /*#__PURE__*/React.createElement(Slot, {
|
27
|
+
name: "One"
|
28
|
+
}, children);
|
29
|
+
|
30
|
+
SlotItem1.displayName = "SlotItem1";
|
31
|
+
|
32
|
+
const SlotItem2 = ({
|
33
|
+
children
|
34
|
+
}) => /*#__PURE__*/React.createElement(Slot, {
|
35
|
+
name: "Two"
|
36
|
+
}, children);
|
37
|
+
|
38
|
+
SlotItem2.displayName = "SlotItem2";
|
39
|
+
|
40
|
+
const SlotItem3 = ({
|
41
|
+
children
|
42
|
+
}) => /*#__PURE__*/React.createElement(Slot, {
|
43
|
+
name: "Three"
|
44
|
+
}, context => /*#__PURE__*/React.createElement(React.Fragment, null, context.salutation, " ", children));
|
45
|
+
|
46
|
+
SlotItem3.displayName = "SlotItem3";
|
47
|
+
describe('ComponentWithSlots', () => {
|
48
|
+
it('renders all slots', async () => {
|
49
|
+
const component = render( /*#__PURE__*/React.createElement(ComponentWithSlots, null, /*#__PURE__*/React.createElement(SlotItem1, null, "first"), /*#__PURE__*/React.createElement(SlotItem2, null, "second"), "free form"));
|
50
|
+
await waitFor(() => component.getByText('first'));
|
51
|
+
expect(component.container).toMatchSnapshot();
|
52
|
+
});
|
53
|
+
it('renders without any slots', async () => {
|
54
|
+
const component = render( /*#__PURE__*/React.createElement(ComponentWithSlots, null, "free form"));
|
55
|
+
expect(component.container).toMatchSnapshot();
|
56
|
+
});
|
57
|
+
it('renders with just one slot', async () => {
|
58
|
+
const component = render( /*#__PURE__*/React.createElement(ComponentWithSlots, null, /*#__PURE__*/React.createElement(SlotItem1, null, "first"), "free form"));
|
59
|
+
expect(component.container).toMatchSnapshot();
|
60
|
+
});
|
61
|
+
it('renders with context passed to children', async () => {
|
62
|
+
const component = render( /*#__PURE__*/React.createElement(ComponentWithSlots, {
|
63
|
+
salutation: "hi"
|
64
|
+
}, /*#__PURE__*/React.createElement(SlotItem3, null, "third"), "free form"));
|
65
|
+
expect(component.container).toMatchSnapshot();
|
66
|
+
});
|
67
|
+
});
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
2
2
|
import { getAnchoredPosition } from '../behaviors/anchoredPosition';
|
3
3
|
import { useProvidedRefOrCreate } from './useProvidedRefOrCreate';
|
4
4
|
import { useResizeObserver } from './useResizeObserver';
|
5
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect';
|
5
6
|
|
6
7
|
/**
|
7
8
|
* Calculates the top and left values for an absolutely-positioned floating element
|
@@ -24,7 +25,7 @@ export function useAnchoredPosition(settings, dependencies = []) {
|
|
24
25
|
}
|
25
26
|
}, // eslint-disable-next-line react-hooks/exhaustive-deps
|
26
27
|
[floatingElementRef, anchorElementRef, ...dependencies]);
|
27
|
-
|
28
|
+
useLayoutEffect(updatePosition, [updatePosition]);
|
28
29
|
useResizeObserver(updatePosition);
|
29
30
|
return {
|
30
31
|
floatingElementRef,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
import { ForwardedRef } from 'react';
|
2
2
|
/**
|
3
3
|
* Creates a ref by combining multiple constituent refs. The ref returned by this hook
|
4
4
|
* should be passed as the ref for the element that needs to be shared. This is
|
@@ -7,4 +7,4 @@ import React, { ForwardedRef } from 'react';
|
|
7
7
|
* though, as it breaks encapsulation.
|
8
8
|
* @param refs
|
9
9
|
*/
|
10
|
-
export declare function useCombinedRefs<T>(...refs: (ForwardedRef<T> | null | undefined)[]):
|
10
|
+
export declare function useCombinedRefs<T>(...refs: (ForwardedRef<T> | null | undefined)[]): import("react").MutableRefObject<T | null>;
|
@@ -1,4 +1,5 @@
|
|
1
|
-
import
|
1
|
+
import { useRef } from 'react';
|
2
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect';
|
2
3
|
/**
|
3
4
|
* Creates a ref by combining multiple constituent refs. The ref returned by this hook
|
4
5
|
* should be passed as the ref for the element that needs to be shared. This is
|
@@ -10,7 +11,7 @@ import React, { useRef } from 'react';
|
|
10
11
|
|
11
12
|
export function useCombinedRefs(...refs) {
|
12
13
|
const combinedRef = useRef(null);
|
13
|
-
|
14
|
+
useLayoutEffect(() => {
|
14
15
|
function setRefs(current = null) {
|
15
16
|
for (const ref of refs) {
|
16
17
|
if (!ref) {
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import
|
1
|
+
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect';
|
2
2
|
export function useResizeObserver(callback) {
|
3
|
-
|
3
|
+
useLayoutEffect(() => {
|
4
4
|
const observer = new window.ResizeObserver(() => callback());
|
5
5
|
observer.observe(document.documentElement);
|
6
6
|
return () => {
|