@pushwoosh/dumb-components 1.1.229 → 1.1.231
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/RichMessageEditor/RichMessageEditor.js +4 -0
- package/RichMessageEditor/RichMessageEditor.types.d.ts +4 -0
- package/RichMessageEditor/slate/SlateEditor.d.ts +2 -0
- package/RichMessageEditor/slate/SlateEditor.js +4 -0
- package/RichMessageEditor/slate/SlateEditor.styled.d.ts +1 -0
- package/RichMessageEditor/slate/SlateEditor.styled.js +4 -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
|
+
}
|
|
@@ -25,7 +25,9 @@ export const RichMessageEditor = ({
|
|
|
25
25
|
title,
|
|
26
26
|
initialValue,
|
|
27
27
|
minHeight,
|
|
28
|
+
placeholder,
|
|
28
29
|
error,
|
|
30
|
+
isErrored,
|
|
29
31
|
canUsePersonalization,
|
|
30
32
|
canUseEmoji,
|
|
31
33
|
onChange,
|
|
@@ -123,6 +125,8 @@ export const RichMessageEditor = ({
|
|
|
123
125
|
}),
|
|
124
126
|
children: [_jsx(SlateEditable, {
|
|
125
127
|
minHeight: minHeight,
|
|
128
|
+
placeholder: placeholder,
|
|
129
|
+
isErrored: isErrored || !!error,
|
|
126
130
|
onFocus: () => {
|
|
127
131
|
setIsFocused(true);
|
|
128
132
|
const {
|
|
@@ -24,8 +24,12 @@ export type RichMessageEditorProps = {
|
|
|
24
24
|
onChange: (value: string) => void;
|
|
25
25
|
/** Minimum editor height (CSS value). */
|
|
26
26
|
minHeight?: string;
|
|
27
|
+
/** Hint shown while the editor is empty. */
|
|
28
|
+
placeholder?: string;
|
|
27
29
|
/** Error message; switches the field to the error state. */
|
|
28
30
|
error?: ReactNode;
|
|
31
|
+
/** Draw the error border without an error message. */
|
|
32
|
+
isErrored?: boolean;
|
|
29
33
|
/** Enable inserting personalization (dynamic-content) tokens. */
|
|
30
34
|
canUsePersonalization?: boolean;
|
|
31
35
|
/** Enable the emoji picker. */
|
|
@@ -26,6 +26,8 @@ export const SlateEditor = ({
|
|
|
26
26
|
};
|
|
27
27
|
export const SlateEditable = ({
|
|
28
28
|
minHeight,
|
|
29
|
+
placeholder,
|
|
30
|
+
isErrored,
|
|
29
31
|
onFocus,
|
|
30
32
|
onDelete
|
|
31
33
|
}) => {
|
|
@@ -34,6 +36,8 @@ export const SlateEditable = ({
|
|
|
34
36
|
});
|
|
35
37
|
return _jsx(EditableStyled, {
|
|
36
38
|
"$minHeight": minHeight,
|
|
39
|
+
"$isErrored": isErrored,
|
|
40
|
+
placeholder: placeholder,
|
|
37
41
|
renderElement: renderElementPersistent,
|
|
38
42
|
onFocus: onFocus
|
|
39
43
|
});
|
|
@@ -15,4 +15,5 @@ export declare const EditableStyled: import("styled-components").StyledComponent
|
|
|
15
15
|
disableDefaultStyles?: boolean | undefined;
|
|
16
16
|
} & import("react").TextareaHTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>, any, {
|
|
17
17
|
$minHeight?: string;
|
|
18
|
+
$isErrored?: boolean;
|
|
18
19
|
}, never>;
|
|
@@ -4,6 +4,8 @@ import { Color, FontSize, FontWeight, ShapeRadius } from '@pushwoosh/kit-constan
|
|
|
4
4
|
export const EditableStyled = styled(Editable).withConfig({
|
|
5
5
|
displayName: "EditableStyled",
|
|
6
6
|
componentId: "sc-1frhl1f-0"
|
|
7
|
-
})(["font-weight:", ";font-size:", ";line-height:140%;padding:8px;color:", ";background:", ";caret-color:", ";border:1px solid ", ";border-radius:", ";overflow-x:auto;", " &:focus{border-color:", ";outline:none;}& > p{margin:4px 0;&:first-child{margin-top:0;}&:last-child{margin-bottom:0;}}"], FontWeight.REGULAR, FontSize.REGULAR, Color.MAIN, Color.CLEAR, Color.BRIGHT,
|
|
7
|
+
})(["font-weight:", ";font-size:", ";line-height:140%;padding:8px;color:", ";background:", ";caret-color:", ";border:1px solid ", ";border-radius:", ";overflow-x:auto;", " & [data-slate-placeholder='true']{color:", ";opacity:1 !important;}&:focus{border-color:", ";outline:none;}& > p{position:relative;margin:4px 0;&:first-child{margin-top:0;}&:last-child{margin-bottom:0;}}"], FontWeight.REGULAR, FontSize.REGULAR, Color.MAIN, Color.CLEAR, Color.BRIGHT, ({
|
|
8
|
+
$isErrored
|
|
9
|
+
}) => $isErrored ? Color.DANGER : Color.FORM, ShapeRadius.CONTROL, ({
|
|
8
10
|
$minHeight
|
|
9
|
-
}) => $minHeight && `min-height: ${$minHeight};`, Color.BRIGHT);
|
|
11
|
+
}) => $minHeight && `min-height: ${$minHeight};`, Color.PHANTOM, Color.BRIGHT);
|