@pushwoosh/dumb-components 1.1.230 → 1.1.232
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/DropdownMenu/components/DropdownMenu/DropdownMenu.js +10 -2
- package/DropdownMenu/components/DropdownMenuSubmenu/DropdownMenuSubmenu.js +48 -16
- package/DropdownMenu/components/DropdownMenuSubmenu/types.d.ts +2 -0
- package/DropdownMenu/context.d.ts +7 -0
- package/DropdownMenu/context.js +3 -1
- package/DropdownMenu/shared/styles.d.ts +1 -0
- package/DropdownMenu/shared/styles.js +2 -1
- package/DropdownMenu/shared/useSubmenuRegistry.d.ts +6 -0
- package/DropdownMenu/shared/useSubmenuRegistry.js +23 -0
- package/InboxPreview/styles.js +2 -2
- package/package.json +1 -1
|
@@ -4,8 +4,9 @@ import { useState, useEffect, useCallback } from 'react';
|
|
|
4
4
|
import { createPortal, flushSync } from 'react-dom';
|
|
5
5
|
import { Place } from './styles';
|
|
6
6
|
import { useLayer } from '../../../hooks';
|
|
7
|
-
import { DropdownContext, DropdownZIndexContext } from '../../context';
|
|
7
|
+
import { DropdownColorContext, DropdownContext, DropdownZIndexContext, SubmenuRegistryContext } from '../../context';
|
|
8
8
|
import { renderChildren } from '../../shared/renderChildren';
|
|
9
|
+
import { useSubmenuRegistry } from '../../shared/useSubmenuRegistry';
|
|
9
10
|
const defaultPlacements = ['top-start', 'top-end', 'bottom-start', 'bottom-end'];
|
|
10
11
|
/**
|
|
11
12
|
* Floating dropdown menu anchored to a custom trigger (`renderHandler`), with
|
|
@@ -32,6 +33,7 @@ export const DropdownMenu = ({
|
|
|
32
33
|
children
|
|
33
34
|
}) => {
|
|
34
35
|
const [isOpen, setIsOpen] = useState(openOnMount);
|
|
36
|
+
const submenus = useSubmenuRegistry();
|
|
35
37
|
const [width, setWidth] = useState(windowWidth);
|
|
36
38
|
const handleClose = useCallback(() => {
|
|
37
39
|
setIsOpen(false);
|
|
@@ -111,7 +113,13 @@ export const DropdownMenu = ({
|
|
|
111
113
|
"$fitContent": windowWidth === null && !fullWidth,
|
|
112
114
|
"$scrollable": isScrollable,
|
|
113
115
|
"$zIndex": resolvedZIndex,
|
|
114
|
-
children:
|
|
116
|
+
children: _jsx(DropdownColorContext.Provider, {
|
|
117
|
+
value: color,
|
|
118
|
+
children: _jsx(SubmenuRegistryContext.Provider, {
|
|
119
|
+
value: submenus,
|
|
120
|
+
children: renderChildren(children)
|
|
121
|
+
})
|
|
122
|
+
})
|
|
115
123
|
})
|
|
116
124
|
}), document.body)]
|
|
117
125
|
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { flip, shift, useFloating } from '@floating-ui/react-dom';
|
|
3
|
-
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { autoUpdate, flip, shift, size, useFloating } from '@floating-ui/react-dom';
|
|
3
|
+
import { useCallback, useContext, useEffect, useId, useRef, useState } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { ChevronIcon } from '@pushwoosh/kit-icons';
|
|
6
6
|
import { SubmenuTriggerInner } from './styles';
|
|
7
|
-
import { DropdownContext, DropdownZIndexContext } from '../../context';
|
|
7
|
+
import { DropdownColorContext, DropdownContext, DropdownZIndexContext, SubmenuRegistryContext } from '../../context';
|
|
8
8
|
import { renderChildren } from '../../shared/renderChildren';
|
|
9
|
-
import { DropdownMenuItemStyled, Place } from '../../shared/styles';
|
|
9
|
+
import { DropdownMenuItemStyled, Place, SCROLLABLE_MAX_HEIGHT } from '../../shared/styles';
|
|
10
|
+
import { useSubmenuRegistry } from '../../shared/useSubmenuRegistry';
|
|
10
11
|
const OPEN_DELAY = 100;
|
|
11
12
|
const CLOSE_DELAY = 200;
|
|
12
13
|
/**
|
|
@@ -22,25 +23,35 @@ export const DropdownMenuSubmenu = ({
|
|
|
22
23
|
label,
|
|
23
24
|
isDanger,
|
|
24
25
|
disabled,
|
|
26
|
+
isScrollable,
|
|
25
27
|
children
|
|
26
28
|
}) => {
|
|
27
29
|
const parentClose = useContext(DropdownContext);
|
|
28
30
|
const parentZIndex = useContext(DropdownZIndexContext);
|
|
31
|
+
const parentSubmenus = useContext(SubmenuRegistryContext);
|
|
32
|
+
const color = useContext(DropdownColorContext);
|
|
33
|
+
const submenus = useSubmenuRegistry();
|
|
34
|
+
const id = useId();
|
|
29
35
|
const submenuZIndex = (parentZIndex ?? 10001) + 1;
|
|
30
36
|
const [isOpen, setIsOpen] = useState(false);
|
|
31
37
|
const openedByClick = useRef(false);
|
|
38
|
+
const [availableHeight, setAvailableHeight] = useState();
|
|
32
39
|
const {
|
|
33
40
|
refs,
|
|
34
41
|
floatingStyles
|
|
35
42
|
} = useFloating({
|
|
36
43
|
strategy: 'fixed',
|
|
37
44
|
placement: 'right-start',
|
|
45
|
+
whileElementsMounted: autoUpdate,
|
|
38
46
|
middleware: [flip({
|
|
39
|
-
fallbackPlacements: ['left-start']
|
|
40
|
-
boundary: document.body
|
|
47
|
+
fallbackPlacements: ['left-start']
|
|
41
48
|
}), shift({
|
|
49
|
+
padding: 4
|
|
50
|
+
}), size({
|
|
42
51
|
padding: 4,
|
|
43
|
-
|
|
52
|
+
apply: ({
|
|
53
|
+
availableHeight: height
|
|
54
|
+
}) => setAvailableHeight(height)
|
|
44
55
|
})]
|
|
45
56
|
});
|
|
46
57
|
const openTimer = useRef(null);
|
|
@@ -52,6 +63,18 @@ export const DropdownMenuSubmenu = ({
|
|
|
52
63
|
closeTimer.current = null;
|
|
53
64
|
}, []);
|
|
54
65
|
useEffect(() => () => clearTimers(), [clearTimers]);
|
|
66
|
+
const maxHeight = isScrollable ? Math.min(availableHeight ?? SCROLLABLE_MAX_HEIGHT, SCROLLABLE_MAX_HEIGHT) : availableHeight;
|
|
67
|
+
const close = useCallback(() => {
|
|
68
|
+
clearTimers();
|
|
69
|
+
openedByClick.current = false;
|
|
70
|
+
setIsOpen(false);
|
|
71
|
+
}, [clearTimers]);
|
|
72
|
+
useEffect(() => parentSubmenus?.register(id, close), [parentSubmenus, id, close]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (isOpen) {
|
|
75
|
+
parentSubmenus?.closeOthers(id);
|
|
76
|
+
}
|
|
77
|
+
}, [isOpen, parentSubmenus, id]);
|
|
55
78
|
const scheduleOpen = useCallback(() => {
|
|
56
79
|
if (disabled) return;
|
|
57
80
|
openedByClick.current = false;
|
|
@@ -94,15 +117,24 @@ export const DropdownMenuSubmenu = ({
|
|
|
94
117
|
size: "small"
|
|
95
118
|
})]
|
|
96
119
|
})
|
|
97
|
-
}), isOpen && createPortal(_jsx(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
}), isOpen && createPortal(_jsx(SubmenuRegistryContext.Provider, {
|
|
121
|
+
value: submenus,
|
|
122
|
+
children: _jsx(Place, {
|
|
123
|
+
ref: refs.setFloating,
|
|
124
|
+
style: {
|
|
125
|
+
...floatingStyles,
|
|
126
|
+
maxHeight,
|
|
127
|
+
overflowY: maxHeight ? 'auto' : undefined
|
|
128
|
+
},
|
|
129
|
+
"$color": color,
|
|
130
|
+
"$scrollable": isScrollable,
|
|
131
|
+
"$fitContent": true,
|
|
132
|
+
"$zIndex": submenuZIndex,
|
|
133
|
+
onMouseEnter: clearTimers,
|
|
134
|
+
onMouseLeave: scheduleClose,
|
|
135
|
+
onClick: e => e.stopPropagation(),
|
|
136
|
+
children: renderChildren(children)
|
|
137
|
+
})
|
|
106
138
|
}), document.body)]
|
|
107
139
|
})
|
|
108
140
|
});
|
|
@@ -7,6 +7,8 @@ export type DropdownMenuSubmenuProps = {
|
|
|
7
7
|
isDanger?: boolean;
|
|
8
8
|
/** Disable the submenu. */
|
|
9
9
|
disabled?: boolean;
|
|
10
|
+
/** Cap the flyout height and scroll its contents. */
|
|
11
|
+
isScrollable?: boolean;
|
|
10
12
|
/** Submenu contents — nodes, or a function returning nodes. */
|
|
11
13
|
children: (() => ReactNode) | ReactNode;
|
|
12
14
|
};
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
import type { Color } from '@pushwoosh/kit-constants';
|
|
1
2
|
export declare const DropdownContext: import("react").Context<((isOpen: false) => void) | null>;
|
|
2
3
|
export declare const DropdownZIndexContext: import("react").Context<number | undefined>;
|
|
4
|
+
export type SubmenuRegistry = {
|
|
5
|
+
register: (id: string, close: () => void) => () => void;
|
|
6
|
+
closeOthers: (id: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare const SubmenuRegistryContext: import("react").Context<SubmenuRegistry | null>;
|
|
9
|
+
export declare const DropdownColorContext: import("react").Context<Color.FROZEN | Color.CLEAR | undefined>;
|
package/DropdownMenu/context.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { createContext } from 'react';
|
|
2
2
|
export const DropdownContext = createContext(null);
|
|
3
|
-
export const DropdownZIndexContext = createContext(undefined);
|
|
3
|
+
export const DropdownZIndexContext = createContext(undefined);
|
|
4
|
+
export const SubmenuRegistryContext = createContext(null);
|
|
5
|
+
export const DropdownColorContext = createContext(undefined);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const SCROLLABLE_MAX_HEIGHT = 248;
|
|
1
2
|
export declare const Place: import("styled-components").StyledComponent<"div", any, import("@pushwoosh/kit-helpers/styled-helpers").CommonBoxProps & {
|
|
2
3
|
$alignContent?: import("@pushwoosh/kit-helpers/styled-helpers").AlignContentValue | undefined;
|
|
3
4
|
$alignItems?: import("@pushwoosh/kit-helpers/styled-helpers").ItemAlignmentValue | undefined;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import styled, { css } from 'styled-components';
|
|
2
2
|
import { Color, Shadow, ShapeRadius, UnitSize } from '@pushwoosh/kit-constants';
|
|
3
3
|
import { Vertical } from '@pushwoosh/kit-helpers';
|
|
4
|
-
const
|
|
4
|
+
export const SCROLLABLE_MAX_HEIGHT = 248;
|
|
5
|
+
const scrollableCss = css(["max-height:", "px;overflow-y:auto;"], SCROLLABLE_MAX_HEIGHT);
|
|
5
6
|
const getWidth = ($width, $fitContent) => {
|
|
6
7
|
if ($fitContent) return 'max-content';
|
|
7
8
|
if ($width) return `${$width}px`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SubmenuRegistry } from '../context';
|
|
2
|
+
/**
|
|
3
|
+
* Registry of the submenus rendered on one menu level: opening a submenu closes
|
|
4
|
+
* its siblings through it, and never the branch it hangs from.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useSubmenuRegistry(): SubmenuRegistry;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Registry of the submenus rendered on one menu level: opening a submenu closes
|
|
4
|
+
* its siblings through it, and never the branch it hangs from.
|
|
5
|
+
*/
|
|
6
|
+
export function useSubmenuRegistry() {
|
|
7
|
+
const entries = useRef(new Map());
|
|
8
|
+
return useMemo(() => ({
|
|
9
|
+
register: (id, close) => {
|
|
10
|
+
entries.current.set(id, close);
|
|
11
|
+
return () => {
|
|
12
|
+
entries.current.delete(id);
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
closeOthers: id => {
|
|
16
|
+
entries.current.forEach((close, key) => {
|
|
17
|
+
if (key !== id) {
|
|
18
|
+
close();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}), []);
|
|
23
|
+
}
|
package/InboxPreview/styles.js
CHANGED
|
@@ -5,14 +5,14 @@ import { Text } from '@pushwoosh/kit-typography';
|
|
|
5
5
|
export const HeroImage = styled.img.withConfig({
|
|
6
6
|
displayName: "HeroImage",
|
|
7
7
|
componentId: "sc-1rsolhm-0"
|
|
8
|
-
})(["display:block;width:100%;aspect-ratio:
|
|
8
|
+
})(["display:block;width:100%;aspect-ratio:2 / 1;object-fit:cover;border-radius:", ";"], ShapeRadius.SECTION);
|
|
9
9
|
export const EmptyHero = styled(Vertical).attrs({
|
|
10
10
|
$borderRadius: ShapeRadius.SECTION,
|
|
11
11
|
$shrink: 0
|
|
12
12
|
}).withConfig({
|
|
13
13
|
displayName: "EmptyHero",
|
|
14
14
|
componentId: "sc-1rsolhm-1"
|
|
15
|
-
})(["aspect-ratio:
|
|
15
|
+
})(["aspect-ratio:2 / 1;border:1px dashed ", ";background:", ";"], Color.BRIGHT, Color.BRIGHT_LIGHT_HOVER);
|
|
16
16
|
export const ClampText = styled(Text).withConfig({
|
|
17
17
|
displayName: "ClampText",
|
|
18
18
|
componentId: "sc-1rsolhm-2"
|