@linagora/twake-mui 10.4.0 → 10.5.0
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 +15 -0
- package/dist/components/BottomSheet/BottomSheet.stories.d.ts +23 -0
- package/dist/components/BottomSheet/BottomSheet.stories.js +139 -0
- package/dist/components/BottomSheet/BottomSheetHeader.d.ts +9 -0
- package/dist/components/BottomSheet/BottomSheetHeader.js +14 -0
- package/dist/components/BottomSheet/BottomSheetItem.d.ts +10 -0
- package/dist/components/BottomSheet/BottomSheetItem.js +18 -0
- package/dist/components/BottomSheet/BottomSheetTitle.d.ts +10 -0
- package/dist/components/BottomSheet/BottomSheetTitle.js +20 -0
- package/dist/components/BottomSheet/core/BottomSheetCore.d.ts +28 -0
- package/dist/components/BottomSheet/core/BottomSheetCore.js +118 -0
- package/dist/components/BottomSheet/core/helpers.d.ts +8 -0
- package/dist/components/BottomSheet/core/helpers.js +29 -0
- package/dist/components/BottomSheet/helpers.d.ts +14 -0
- package/dist/components/BottomSheet/helpers.js +95 -0
- package/dist/components/BottomSheet/index.d.ts +8 -0
- package/dist/components/BottomSheet/index.js +158 -0
- package/dist/components/BottomSheet/safeArea.d.ts +3 -0
- package/dist/components/BottomSheet/safeArea.js +10 -0
- package/dist/components/BottomSheet/styles.d.ts +25 -0
- package/dist/components/BottomSheet/styles.js +100 -0
- package/dist/components/BottomSheet/types.d.ts +77 -0
- package/dist/components/BottomSheet/types.js +1 -0
- package/dist/components/ContactPopover/AttendeeInfo.js +3 -3
- package/dist/components/ContactPopover/ContactPopoverCalendarAction.js +2 -2
- package/dist/components/ContactPopover/ContactPopoverChatAction.js +2 -2
- package/dist/components/ContactPopover/ContactPopoverEmailAction.js +2 -2
- package/dist/components/ContactPopover/ContactPopoverVideoAction.js +2 -2
- package/dist/components/SearchBar/DisableHighlight.js +1 -1
- package/dist/components/SearchBar/FocusHighlight.js +1 -1
- package/dist/components/SearchBar/SearchBarIconWrapper.js +1 -1
- package/dist/components/SearchBar/SearchBarRoot.js +4 -4
- package/dist/components/SpaceOverlay/OverlayFrame.js +17 -21
- package/dist/components/SpaceOverlay/SpaceOverlay.js +2 -2
- package/dist/index.d.ts +16 -1
- package/dist/index.js +8 -1
- package/dist/lib/overrides.js +21 -19
- package/package.json +4 -1
- package/dist/components/SpaceOverlay/index.d.ts +0 -8
- package/dist/components/SpaceOverlay/index.js +0 -4
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Backdrop, Portal } from '@mui/material';
|
|
2
|
+
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useMutationObserver, useTimeoutWhen } from 'rooks';
|
|
4
|
+
import { ANIMATION_DURATION, computeBottomSpacer, computeMaxHeight, computeMediumHeight, computeMinHeight, getCssValue, makeOverriddenChildren, minimizeAndClose, setBottomPosition, setTopPosition } from './helpers';
|
|
5
|
+
import { BORDER_RADIUS, BottomSheetBounceSafer, BottomSheetContainer, BottomSheetHandleBar, BottomSheetIndicator, BottomSheetOffsetSafer, BottomSheetRenderSafer, BottomSheetRenderSaferFill, BottomSheetRoot, BottomSheetSpacer, BottomSheetStack } from './styles';
|
|
6
|
+
/** Shared default, so that effects depending on it do not run on every render */
|
|
7
|
+
const NO_TOOLBAR_PROPS = {};
|
|
8
|
+
const DEFAULT_MEDIUM_HEIGHT_RATIO = 0.75;
|
|
9
|
+
const SPRING_CONFIG = { tension: 165, friction: 17, clamp: true };
|
|
10
|
+
const BottomSheetContent = memo(function BottomSheetContent({ toolbarProps = NO_TOOLBAR_PROPS, settings, backdrop = false, skipAnimation, onClose, offset = 0, children }) {
|
|
11
|
+
const mediumHeight = settings?.mediumHeight ?? null;
|
|
12
|
+
const mediumHeightRatio = settings?.mediumHeightRatio ?? DEFAULT_MEDIUM_HEIGHT_RATIO;
|
|
13
|
+
const isOpenMin = settings?.isOpenMin ?? false;
|
|
14
|
+
const hasMinHeightOffset = settings?.hasMinHeightOffset ?? false;
|
|
15
|
+
const innerContentRef = useRef(null);
|
|
16
|
+
const headerRef = useRef(null);
|
|
17
|
+
const headerContentRef = useRef(null);
|
|
18
|
+
const [isTopPosition, setIsTopPosition] = useState(false);
|
|
19
|
+
const [isBottomPosition, setIsBottomPosition] = useState(false);
|
|
20
|
+
const [isHidden, setIsHidden] = useState(false);
|
|
21
|
+
const [showBackdrop, setShowBackdrop] = useState(backdrop);
|
|
22
|
+
const [peekHeights, setPeekHeights] = useState(null);
|
|
23
|
+
// Starts at the medium stop, the one the initial position is computed from
|
|
24
|
+
const [currentIndex, setCurrentIndex] = useState(1);
|
|
25
|
+
const [bottomSpacerHeight, setBottomSpacerHeight] = useState(0);
|
|
26
|
+
const [initPos, setInitPos] = useState(0);
|
|
27
|
+
const prevInitPos = useRef(0);
|
|
28
|
+
const [forceRender, setForceRender] = useState(0);
|
|
29
|
+
const hasToolbarProps = Object.keys(toolbarProps).length > 0;
|
|
30
|
+
const isClosable = !!onClose || backdrop;
|
|
31
|
+
const renderSaferHeight = initPos < prevInitPos.current ? prevInitPos.current - BORDER_RADIUS : 0;
|
|
32
|
+
const showRenderSafer = prevInitPos.current !== 0 && prevInitPos.current > initPos;
|
|
33
|
+
const overriddenChildren = makeOverriddenChildren(children, headerContentRef);
|
|
34
|
+
if (backdrop && !onClose) {
|
|
35
|
+
throw new Error('BottomSheet must have `onClose` method to work properly when setting `backdrop` to `true`');
|
|
36
|
+
}
|
|
37
|
+
const handleClose = useCallback(() => {
|
|
38
|
+
setShowBackdrop(false);
|
|
39
|
+
setIsHidden(true);
|
|
40
|
+
onClose?.();
|
|
41
|
+
}, [onClose]);
|
|
42
|
+
const handleIndexChange = (snapIndex) => {
|
|
43
|
+
setCurrentIndex(snapIndex);
|
|
44
|
+
setTopPosition({
|
|
45
|
+
snapIndex,
|
|
46
|
+
peekHeights: peekHeights ?? [],
|
|
47
|
+
isTopPosition,
|
|
48
|
+
setIsTopPosition
|
|
49
|
+
});
|
|
50
|
+
setBottomPosition({ snapIndex, isBottomPosition, setIsBottomPosition });
|
|
51
|
+
};
|
|
52
|
+
const handleBackdropClick = () => {
|
|
53
|
+
minimizeAndClose({
|
|
54
|
+
backdrop,
|
|
55
|
+
setCurrentIndex,
|
|
56
|
+
setIsTopPosition,
|
|
57
|
+
setIsBottomPosition,
|
|
58
|
+
handleClose
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
// Recomputes the stops when the content changes, after loading or navigating inside it
|
|
62
|
+
useMutationObserver(innerContentRef, () => {
|
|
63
|
+
setForceRender(value => value + 1);
|
|
64
|
+
});
|
|
65
|
+
// Prevents the pull-to-refresh of iOS Safari while dragging the sheet down
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
document.body.style.overflow = 'hidden';
|
|
68
|
+
return () => {
|
|
69
|
+
document.body.style.overflow = 'auto';
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
useTimeoutWhen(() => handleClose(), ANIMATION_DURATION, isClosable && isBottomPosition);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
const headerContent = headerContentRef.current;
|
|
75
|
+
const innerContentHeight = innerContentRef.current?.offsetHeight ?? 0;
|
|
76
|
+
const actionButtonsHeight = getCssValue(headerContent, 'height');
|
|
77
|
+
const actionButtonsBottomMargin = getCssValue(headerContent, 'padding-bottom');
|
|
78
|
+
const maxHeight = computeMaxHeight(toolbarProps);
|
|
79
|
+
const newBottomSpacerHeight = computeBottomSpacer({
|
|
80
|
+
backdrop,
|
|
81
|
+
maxHeight,
|
|
82
|
+
innerContentHeight,
|
|
83
|
+
toolbarProps,
|
|
84
|
+
offset
|
|
85
|
+
});
|
|
86
|
+
const minHeight = computeMinHeight({
|
|
87
|
+
isClosable,
|
|
88
|
+
isOpenMin,
|
|
89
|
+
headerRef,
|
|
90
|
+
offset: hasMinHeightOffset ? offset : 0,
|
|
91
|
+
actionButtonsHeight,
|
|
92
|
+
actionButtonsBottomMargin
|
|
93
|
+
});
|
|
94
|
+
const computedMediumHeight = isOpenMin && actionButtonsHeight > 0
|
|
95
|
+
? minHeight + 1
|
|
96
|
+
: computeMediumHeight({
|
|
97
|
+
backdrop,
|
|
98
|
+
maxHeight,
|
|
99
|
+
mediumHeight,
|
|
100
|
+
mediumHeightRatio,
|
|
101
|
+
innerContentHeight,
|
|
102
|
+
bottomSpacerHeight: newBottomSpacerHeight,
|
|
103
|
+
offset
|
|
104
|
+
});
|
|
105
|
+
const newPeekHeights = [
|
|
106
|
+
...new Set([minHeight, computedMediumHeight, maxHeight])
|
|
107
|
+
];
|
|
108
|
+
const hasPeekHeightsChanged = peekHeights?.toString() !== newPeekHeights.toString();
|
|
109
|
+
// Lowers the sheet when its stops change while it is at the top
|
|
110
|
+
const snapIndex = hasPeekHeightsChanged && isTopPosition
|
|
111
|
+
? currentIndex - 1 - (currentIndex - (newPeekHeights.length - 1))
|
|
112
|
+
: currentIndex;
|
|
113
|
+
setCurrentIndex(snapIndex);
|
|
114
|
+
setPeekHeights(newPeekHeights);
|
|
115
|
+
prevInitPos.current = initPos;
|
|
116
|
+
setInitPos(computedMediumHeight);
|
|
117
|
+
setTopPosition({
|
|
118
|
+
snapIndex,
|
|
119
|
+
peekHeights: newPeekHeights,
|
|
120
|
+
isTopPosition,
|
|
121
|
+
setIsTopPosition
|
|
122
|
+
});
|
|
123
|
+
// Lets the sheet open up to the top without stopping at the content height
|
|
124
|
+
setBottomSpacerHeight(newBottomSpacerHeight);
|
|
125
|
+
// initPos is left out on purpose: the effect needs its previous value
|
|
126
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
127
|
+
}, [
|
|
128
|
+
innerContentRef,
|
|
129
|
+
toolbarProps,
|
|
130
|
+
mediumHeightRatio,
|
|
131
|
+
mediumHeight,
|
|
132
|
+
showBackdrop,
|
|
133
|
+
backdrop,
|
|
134
|
+
isClosable,
|
|
135
|
+
offset,
|
|
136
|
+
forceRender
|
|
137
|
+
]);
|
|
138
|
+
const content = (React.createElement(React.Fragment, null,
|
|
139
|
+
showBackdrop && React.createElement(Backdrop, { open: true, onClick: handleBackdropClick }),
|
|
140
|
+
React.createElement(BottomSheetRoot, { isTopPosition: isTopPosition, hasToolbar: hasToolbarProps, peekHeights: peekHeights, defaultHeight: initPos, fullHeight: !hasToolbarProps, currentIndex: currentIndex, onIndexChange: handleIndexChange, threshold: 0, springConfig: SPRING_CONFIG, disabledClosing: !onClose, hidden: isHidden, snapPointSeekerMode: "next", skipAnimation: skipAnimation },
|
|
141
|
+
React.createElement("div", { ref: innerContentRef },
|
|
142
|
+
React.createElement(BottomSheetHandleBar, { "data-testid": "bottomSheet-header", ref: headerRef, elevation: 0, square: true },
|
|
143
|
+
React.createElement(BottomSheetIndicator, null)),
|
|
144
|
+
React.createElement(BottomSheetStack, { spacing: 1.5 }, overriddenChildren)),
|
|
145
|
+
React.createElement(BottomSheetSpacer, { spacerHeight: bottomSpacerHeight })),
|
|
146
|
+
showRenderSafer && (React.createElement(BottomSheetRenderSafer, { saferHeight: renderSaferHeight },
|
|
147
|
+
React.createElement(BottomSheetRenderSaferFill, { elevation: 0, square: true }))),
|
|
148
|
+
!isBottomPosition && React.createElement(BottomSheetBounceSafer, null),
|
|
149
|
+
Boolean(offset) && (React.createElement(BottomSheetOffsetSafer, { offset: offset, isBottomPosition: isBottomPosition }))));
|
|
150
|
+
return showBackdrop ? (React.createElement(BottomSheetContainer, null, content)) : (content);
|
|
151
|
+
});
|
|
152
|
+
/**
|
|
153
|
+
* Panel coming up from the bottom of the screen, that the user can drag between a minimum, a
|
|
154
|
+
* medium and a full height. Rendered in a portal, like dialogs.
|
|
155
|
+
*/
|
|
156
|
+
const BottomSheet = ({ portalProps, ...props }) => (React.createElement(Portal, { ...portalProps },
|
|
157
|
+
React.createElement(BottomSheetContent, { ...props })));
|
|
158
|
+
export default BottomSheet;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Returns the device safe area inset (notch, home indicator), in px */
|
|
2
|
+
export function getSafeAreaInset(side) {
|
|
3
|
+
// env() is only resolved through a style, so a probe element reads it
|
|
4
|
+
const probe = document.createElement('div');
|
|
5
|
+
probe.style.paddingTop = `env(safe-area-inset-${side})`;
|
|
6
|
+
document.body.appendChild(probe);
|
|
7
|
+
const inset = parseFloat(getComputedStyle(probe).paddingTop) || 0;
|
|
8
|
+
probe.remove();
|
|
9
|
+
return inset;
|
|
10
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Theme } from '@mui/material/styles';
|
|
2
|
+
export declare const BORDER_RADIUS = 16;
|
|
3
|
+
export declare const BottomSheetContainer: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
4
|
+
export declare const BottomSheetRoot: import("@emotion/styled").StyledComponent<import("./core/BottomSheetCore").BottomSheetCoreProps & import("@mui/system").MUIStyledCommonProps<Theme> & {
|
|
5
|
+
isTopPosition: boolean;
|
|
6
|
+
hasToolbar: boolean;
|
|
7
|
+
}, {}, {}>;
|
|
8
|
+
export declare const BottomSheetHandleBar: import("@emotion/styled").StyledComponent<import("@mui/material").PaperOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | "square" | "className" | "elevation" | "children" | "classes" | "sx" | "variant"> & import("@mui/system").MUIStyledCommonProps<Theme>, {}, {}>;
|
|
9
|
+
export declare const BottomSheetIndicator: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
10
|
+
export declare const BottomSheetStack: import("@emotion/styled").StyledComponent<import("@mui/material").StackOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof import("@mui/material/OverridableComponent").CommonProps | keyof import("@mui/material").StackOwnProps> & import("@mui/system").MUIStyledCommonProps<Theme>, {}, {}>;
|
|
11
|
+
export declare const BottomSheetSpacer: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme> & {
|
|
12
|
+
spacerHeight: number;
|
|
13
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
14
|
+
/** Covers the screen bottom while the sheet bounces up */
|
|
15
|
+
export declare const BottomSheetBounceSafer: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme>, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
16
|
+
/** Covers the offset area below the sheet */
|
|
17
|
+
export declare const BottomSheetOffsetSafer: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme> & {
|
|
18
|
+
offset: number;
|
|
19
|
+
isBottomPosition: boolean;
|
|
20
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
21
|
+
/** Hides the gap left under the sheet while it shrinks to a lower content height */
|
|
22
|
+
export declare const BottomSheetRenderSafer: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<Theme> & {
|
|
23
|
+
saferHeight: number;
|
|
24
|
+
}, import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
|
|
25
|
+
export declare const BottomSheetRenderSaferFill: import("@emotion/styled").StyledComponent<import("@mui/material").PaperOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style" | "square" | "className" | "elevation" | "children" | "classes" | "sx" | "variant"> & import("@mui/system").MUIStyledCommonProps<Theme>, {}, {}>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Paper, Stack } from '@mui/material';
|
|
2
|
+
import { keyframes, styled } from '@mui/material/styles';
|
|
3
|
+
import { BottomSheetCore } from './core/BottomSheetCore';
|
|
4
|
+
import { ANIMATION_DURATION } from './helpers';
|
|
5
|
+
export const BORDER_RADIUS = 16;
|
|
6
|
+
const BOUNCE_SAFER_HEIGHT = 50;
|
|
7
|
+
const fadeIn = keyframes({ from: { opacity: 0 }, to: { opacity: 1 } });
|
|
8
|
+
const collapse = keyframes({ from: { height: '100%' }, to: { height: 0 } });
|
|
9
|
+
export const BottomSheetContainer = styled('div')(({ theme }) => ({
|
|
10
|
+
position: 'fixed',
|
|
11
|
+
zIndex: theme.zIndex.modal,
|
|
12
|
+
inset: 0
|
|
13
|
+
}));
|
|
14
|
+
export const BottomSheetRoot = styled(BottomSheetCore, {
|
|
15
|
+
shouldForwardProp: prop => prop !== 'isTopPosition' && prop !== 'hasToolbar'
|
|
16
|
+
})(({ theme }) => ({
|
|
17
|
+
borderTopLeftRadius: BORDER_RADIUS,
|
|
18
|
+
borderTopRightRadius: BORDER_RADIUS,
|
|
19
|
+
transition: 'border-radius 0.5s',
|
|
20
|
+
boxShadow: '0 -0.5px 0px 0 rgba(0, 0, 0, 0.10), 0 -2px 2px 0 rgba(0, 0, 0, 0.02), 0 -4px 4px 0 rgba(0, 0, 0, 0.02), 0 -8px 8px 0 rgba(0, 0, 0, 0.02), 0 -16px 16px 0 rgba(0, 0, 0, 0.02)',
|
|
21
|
+
backgroundColor: theme.vars.palette.background.paper,
|
|
22
|
+
zIndex: theme.zIndex.modal,
|
|
23
|
+
variants: [
|
|
24
|
+
{
|
|
25
|
+
props: { isTopPosition: true },
|
|
26
|
+
style: {
|
|
27
|
+
borderTopLeftRadius: 0,
|
|
28
|
+
borderTopRightRadius: 0,
|
|
29
|
+
boxShadow: '0 -1px 0 0 rgba(255, 255, 255, 1)'
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
props: { isTopPosition: true, hasToolbar: true },
|
|
34
|
+
style: { boxShadow: '0 0 1px 0 rgba(0, 0, 0, 0.5)' }
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}));
|
|
38
|
+
export const BottomSheetHandleBar = styled(Paper)({
|
|
39
|
+
width: '100%',
|
|
40
|
+
height: '3rem',
|
|
41
|
+
position: 'relative',
|
|
42
|
+
display: 'flex',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
justifyContent: 'center'
|
|
45
|
+
});
|
|
46
|
+
export const BottomSheetIndicator = styled('div')(({ theme }) => ({
|
|
47
|
+
width: '4rem',
|
|
48
|
+
height: '0.25rem',
|
|
49
|
+
borderRadius: 99,
|
|
50
|
+
backgroundColor: theme.vars.palette.text.secondary
|
|
51
|
+
}));
|
|
52
|
+
export const BottomSheetStack = styled(Stack)(({ theme }) => ({
|
|
53
|
+
overflow: 'hidden',
|
|
54
|
+
backgroundColor: theme.vars.palette.background.default
|
|
55
|
+
}));
|
|
56
|
+
export const BottomSheetSpacer = styled('div', {
|
|
57
|
+
shouldForwardProp: prop => prop !== 'spacerHeight'
|
|
58
|
+
})(({ spacerHeight }) => ({ height: spacerHeight }));
|
|
59
|
+
/** Covers the screen bottom while the sheet bounces up */
|
|
60
|
+
export const BottomSheetBounceSafer = styled('div')(({ theme }) => ({
|
|
61
|
+
height: BOUNCE_SAFER_HEIGHT,
|
|
62
|
+
width: '100%',
|
|
63
|
+
position: 'fixed',
|
|
64
|
+
bottom: 0,
|
|
65
|
+
left: 0,
|
|
66
|
+
backgroundColor: theme.vars.palette.background.paper,
|
|
67
|
+
animation: `${fadeIn} ${ANIMATION_DURATION}ms ${theme.transitions.easing.easeInOut}`
|
|
68
|
+
}));
|
|
69
|
+
/** Covers the offset area below the sheet */
|
|
70
|
+
export const BottomSheetOffsetSafer = styled('div', {
|
|
71
|
+
shouldForwardProp: prop => prop !== 'offset' && prop !== 'isBottomPosition'
|
|
72
|
+
})(({ theme, offset, isBottomPosition }) => ({
|
|
73
|
+
opacity: isBottomPosition ? 0 : 1,
|
|
74
|
+
height: offset,
|
|
75
|
+
width: '100%',
|
|
76
|
+
position: 'fixed',
|
|
77
|
+
bottom: 0,
|
|
78
|
+
left: 0,
|
|
79
|
+
backgroundColor: theme.vars.palette.background.paper,
|
|
80
|
+
zIndex: theme.zIndex.modal + 10,
|
|
81
|
+
transition: `opacity ${ANIMATION_DURATION}ms`,
|
|
82
|
+
animation: `${fadeIn} ${ANIMATION_DURATION}ms ${theme.transitions.easing.easeInOut}`
|
|
83
|
+
}));
|
|
84
|
+
/** Hides the gap left under the sheet while it shrinks to a lower content height */
|
|
85
|
+
export const BottomSheetRenderSafer = styled('div', {
|
|
86
|
+
shouldForwardProp: prop => prop !== 'saferHeight'
|
|
87
|
+
})(({ saferHeight }) => ({
|
|
88
|
+
height: saferHeight,
|
|
89
|
+
width: '100%',
|
|
90
|
+
position: 'fixed',
|
|
91
|
+
bottom: 0,
|
|
92
|
+
pointerEvents: 'none'
|
|
93
|
+
}));
|
|
94
|
+
export const BottomSheetRenderSaferFill = styled(Paper)({
|
|
95
|
+
position: 'absolute',
|
|
96
|
+
bottom: 0,
|
|
97
|
+
height: 0,
|
|
98
|
+
width: '100%',
|
|
99
|
+
animation: `${collapse} 1s`
|
|
100
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { PortalProps } from '@mui/material';
|
|
2
|
+
import React, { RefObject } from 'react';
|
|
3
|
+
export type SafeAreaSide = 'top' | 'bottom';
|
|
4
|
+
export interface BottomSheetToolbarProps {
|
|
5
|
+
/** Ref of the toolbar the sheet must stay under when fully open */
|
|
6
|
+
ref?: RefObject<HTMLElement | null>;
|
|
7
|
+
/** Height of that toolbar, in px, used instead of measuring `ref` */
|
|
8
|
+
height?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface BottomSheetSettings {
|
|
11
|
+
/** Height of the middle stop, in px */
|
|
12
|
+
mediumHeight?: number | null;
|
|
13
|
+
/** Height of the middle stop, as a ratio of the available height */
|
|
14
|
+
mediumHeightRatio?: number;
|
|
15
|
+
/** Includes the offset in the minimum height */
|
|
16
|
+
hasMinHeightOffset?: boolean;
|
|
17
|
+
/** Opens the sheet at its minimum height, when it has a header */
|
|
18
|
+
isOpenMin?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface BottomSheetProps {
|
|
21
|
+
/** Toolbar the sheet must stay under when fully open */
|
|
22
|
+
toolbarProps?: BottomSheetToolbarProps;
|
|
23
|
+
settings?: BottomSheetSettings;
|
|
24
|
+
/** Adds a backdrop, which requires `onClose` */
|
|
25
|
+
backdrop?: boolean;
|
|
26
|
+
/** Removes animations */
|
|
27
|
+
skipAnimation?: boolean;
|
|
28
|
+
/** Space kept at the bottom of the sheet, in px */
|
|
29
|
+
offset?: number;
|
|
30
|
+
/** Lets the user close the sheet by swiping it down */
|
|
31
|
+
onClose?: () => void;
|
|
32
|
+
portalProps?: Omit<PortalProps, 'children'>;
|
|
33
|
+
children?: React.ReactNode;
|
|
34
|
+
}
|
|
35
|
+
export type BottomSheetContentProps = Omit<BottomSheetProps, 'portalProps'>;
|
|
36
|
+
export interface MediumHeightParams {
|
|
37
|
+
backdrop: boolean;
|
|
38
|
+
maxHeight: number;
|
|
39
|
+
mediumHeight?: number | null;
|
|
40
|
+
mediumHeightRatio: number;
|
|
41
|
+
innerContentHeight: number;
|
|
42
|
+
bottomSpacerHeight: number;
|
|
43
|
+
offset: number;
|
|
44
|
+
}
|
|
45
|
+
export interface MinHeightParams {
|
|
46
|
+
isClosable: boolean;
|
|
47
|
+
isOpenMin: boolean;
|
|
48
|
+
headerRef: RefObject<HTMLElement | null>;
|
|
49
|
+
offset?: number;
|
|
50
|
+
actionButtonsHeight: number;
|
|
51
|
+
actionButtonsBottomMargin: number;
|
|
52
|
+
}
|
|
53
|
+
export interface TopPositionParams {
|
|
54
|
+
snapIndex: number;
|
|
55
|
+
peekHeights: number[];
|
|
56
|
+
isTopPosition: boolean;
|
|
57
|
+
setIsTopPosition: (isTopPosition: boolean) => void;
|
|
58
|
+
}
|
|
59
|
+
export interface BottomPositionParams {
|
|
60
|
+
snapIndex: number;
|
|
61
|
+
isBottomPosition: boolean;
|
|
62
|
+
setIsBottomPosition: (isBottomPosition: boolean) => void;
|
|
63
|
+
}
|
|
64
|
+
export interface MinimizeAndCloseParams {
|
|
65
|
+
backdrop: boolean;
|
|
66
|
+
setCurrentIndex: (index: number) => void;
|
|
67
|
+
setIsTopPosition: (isTopPosition: boolean) => void;
|
|
68
|
+
setIsBottomPosition: (isBottomPosition: boolean) => void;
|
|
69
|
+
handleClose: () => void;
|
|
70
|
+
}
|
|
71
|
+
export interface BottomSpacerParams {
|
|
72
|
+
backdrop: boolean;
|
|
73
|
+
maxHeight: number;
|
|
74
|
+
innerContentHeight: number;
|
|
75
|
+
toolbarProps?: BottomSheetToolbarProps;
|
|
76
|
+
offset: number;
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Icon } from '@linagora/twake-icons';
|
|
2
2
|
import { Copy } from '@linagora/twake-icons';
|
|
3
|
-
import { Box, IconButton, Typography, useTheme
|
|
3
|
+
import { Box, IconButton, Typography, useTheme } from '@mui/material';
|
|
4
4
|
import React, { useState } from 'react';
|
|
5
5
|
import { useI18n } from 'twake-i18n';
|
|
6
6
|
import { Avatar } from '../Avatar';
|
|
@@ -28,8 +28,8 @@ export const AttendeeInfo = ({ name, email, onEmailCopy }) => {
|
|
|
28
28
|
name && name !== email ? (React.createElement(Typography, { variant: "body1", sx: { fontWeight: 500, wordBreak: 'break-word' } }, name)) : null,
|
|
29
29
|
React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
|
|
30
30
|
React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { wordBreak: 'break-word' } }, email),
|
|
31
|
-
React.createElement(IconButton, { size: "small", onClick: () => void handleCopy(), sx: { p: 0.5, color:
|
|
31
|
+
React.createElement(IconButton, { size: "small", onClick: () => void handleCopy(), sx: { p: 0.5, color: 'text.primary' }, title: showCopied
|
|
32
32
|
? t('ContactPopover.copiedTooltip')
|
|
33
33
|
: t('ContactPopover.copyTooltip') },
|
|
34
|
-
React.createElement(Icon, { icon: Copy, size: 16, color: theme.palette.text.icon }))))));
|
|
34
|
+
React.createElement(Icon, { icon: Copy, size: 16, color: theme.vars.palette.text.icon }))))));
|
|
35
35
|
};
|
|
@@ -4,6 +4,6 @@ import { IconButton, useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
export const ContactPopoverCalendarAction = ({ url, disabled = false }) => {
|
|
6
6
|
const theme = useTheme();
|
|
7
|
-
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
8
|
-
React.createElement(Icon, { icon: CalendarToday, size: 20, color: theme.palette.text.icon })));
|
|
7
|
+
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: { border: `1px solid ${theme.vars.palette.divider}` } },
|
|
8
|
+
React.createElement(Icon, { icon: CalendarToday, size: 20, color: theme.vars.palette.text.icon })));
|
|
9
9
|
};
|
|
@@ -4,6 +4,6 @@ import { IconButton, useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
export const ContactPopoverChatAction = ({ url, disabled = false }) => {
|
|
6
6
|
const theme = useTheme();
|
|
7
|
-
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
8
|
-
React.createElement(Icon, { icon: Discuss, size: 20, color: theme.palette.text.icon })));
|
|
7
|
+
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: { border: `1px solid ${theme.vars.palette.divider}` } },
|
|
8
|
+
React.createElement(Icon, { icon: Discuss, size: 20, color: theme.vars.palette.text.icon })));
|
|
9
9
|
};
|
|
@@ -6,7 +6,7 @@ export const ContactPopoverEmailAction = ({ url, disabled = false }) => {
|
|
|
6
6
|
const { t } = useI18n();
|
|
7
7
|
return (React.createElement(Button, { href: url, target: "_blank", rel: "noopener noreferrer", variant: "outlined", size: "small", disabled: disabled, sx: {
|
|
8
8
|
justifyContent: 'center',
|
|
9
|
-
color: theme.palette.text.primary,
|
|
10
|
-
borderColor: theme.palette.divider
|
|
9
|
+
color: theme.vars.palette.text.primary,
|
|
10
|
+
borderColor: theme.vars.palette.divider
|
|
11
11
|
} }, t('ContactPopover.emailButton')));
|
|
12
12
|
};
|
|
@@ -4,6 +4,6 @@ import { IconButton, useTheme } from '@mui/material';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
export const ContactPopoverVideoAction = ({ url, disabled = false }) => {
|
|
6
6
|
const theme = useTheme();
|
|
7
|
-
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: { border: `1px solid ${theme.palette.divider}` } },
|
|
8
|
-
React.createElement(Icon, { icon: Camera, size: 20, color: theme.palette.text.icon })));
|
|
7
|
+
return (React.createElement(IconButton, { href: url, target: "_blank", rel: "noopener noreferrer", size: "small", disabled: disabled, sx: { border: `1px solid ${theme.vars.palette.divider}` } },
|
|
8
|
+
React.createElement(Icon, { icon: Camera, size: 20, color: theme.vars.palette.text.icon })));
|
|
9
9
|
};
|
|
@@ -2,5 +2,5 @@ import { styled } from '@mui/material';
|
|
|
2
2
|
import { Highlight } from './Highlight';
|
|
3
3
|
export const FocusHighlight = styled(Highlight)(({ theme }) => ({
|
|
4
4
|
pointerEvents: 'none',
|
|
5
|
-
backgroundColor: theme.palette.action.hover
|
|
5
|
+
backgroundColor: theme.vars.palette.action.hover
|
|
6
6
|
}));
|
|
@@ -15,10 +15,10 @@ export const SearchBarRoot = styled(Paper, {
|
|
|
15
15
|
borderStyle: 'solid',
|
|
16
16
|
borderWidth: 1,
|
|
17
17
|
paddingRight: '0.5rem',
|
|
18
|
-
borderColor: isFocused ? theme.palette.primary.main : 'transparent',
|
|
18
|
+
borderColor: isFocused ? theme.vars.palette.primary.main : 'transparent',
|
|
19
19
|
backgroundColor: isFocused
|
|
20
|
-
? theme.palette.background.paper
|
|
21
|
-
: theme.palette.background.default,
|
|
20
|
+
? theme.vars.palette.background.paper
|
|
21
|
+
: theme.vars.palette.background.default,
|
|
22
22
|
transition: theme.transitions.create(['border-color', 'background-color']),
|
|
23
23
|
'&:hover': {
|
|
24
24
|
'&:not(.Mui-disabled):not(.SearchBar-focused)': {
|
|
@@ -29,7 +29,7 @@ export const SearchBarRoot = styled(Paper, {
|
|
|
29
29
|
},
|
|
30
30
|
'&.Mui-disabled': {
|
|
31
31
|
'& .SearchBar-icon': {
|
|
32
|
-
color: theme.palette.text.disabled
|
|
32
|
+
color: theme.vars.palette.text.disabled
|
|
33
33
|
},
|
|
34
34
|
'& .SearchBar-disableHighlight': {
|
|
35
35
|
opacity: 1
|
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
import { Box } from '@mui/material';
|
|
2
|
-
import React
|
|
3
|
-
import { createPortal } from 'react-dom';
|
|
1
|
+
import { Box, Portal } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
4
3
|
export const OverlayFrame = ({ name, src, title, sandbox, allow, clipPath }) => {
|
|
5
|
-
const ref = useRef(null);
|
|
6
4
|
const empty = clipPath.startsWith('inset(');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
zIndex: theme => theme.zIndex.modal - 1
|
|
23
|
-
} }), document.body);
|
|
5
|
+
return (React.createElement(Portal, null,
|
|
6
|
+
React.createElement(Box, { component: "iframe", name: name, src: src, title: title, sandbox: sandbox, allow: allow, "aria-hidden": empty || undefined, tabIndex: empty ? -1 : undefined,
|
|
7
|
+
// Inline: it changes with every move of a menu, a class per value
|
|
8
|
+
// would pile up
|
|
9
|
+
style: { clipPath }, sx: {
|
|
10
|
+
position: 'fixed',
|
|
11
|
+
inset: 0,
|
|
12
|
+
width: '100%',
|
|
13
|
+
height: '100%',
|
|
14
|
+
border: 0,
|
|
15
|
+
background: 'transparent',
|
|
16
|
+
// A frame whose colour scheme differs from the page is painted opaque
|
|
17
|
+
colorScheme: 'normal',
|
|
18
|
+
zIndex: theme => theme.zIndex.modal - 1
|
|
19
|
+
} })));
|
|
24
20
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { Portal } from '@mui/material';
|
|
1
2
|
import React, { createContext, useContext, useSyncExternalStore } from 'react';
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
3
3
|
const SpaceOverlayContext = createContext(null);
|
|
4
4
|
export const SpaceOverlayProvider = ({ overlay, children }) => (React.createElement(SpaceOverlayContext.Provider, { value: overlay }, children));
|
|
5
5
|
const NO_OVERLAY = () => () => undefined;
|
|
@@ -31,7 +31,7 @@ export const OverlayPortal = ({ children }) => {
|
|
|
31
31
|
if (status === 'connecting')
|
|
32
32
|
return null;
|
|
33
33
|
const body = status === 'connected' ? (overlay?.getBody() ?? null) : null;
|
|
34
|
-
return React.createElement(
|
|
34
|
+
return (React.createElement(Portal, { container: body, disablePortal: body === null }, children));
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
37
|
* Theme options sending every dialog and temporary drawer to the overlay, so
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export { radius } from './lib/radius';
|
|
|
8
8
|
export { TwakeMuiThemeProvider } from './components/ThemeProvider';
|
|
9
9
|
export { Avatar, default as AvatarDefault } from './components/Avatar';
|
|
10
10
|
export { Button, default as ButtonDefault } from './components/Button';
|
|
11
|
+
export { default as BottomSheet } from './components/BottomSheet';
|
|
12
|
+
export { default as BottomSheetItem } from './components/BottomSheet/BottomSheetItem';
|
|
13
|
+
export { default as BottomSheetHeader } from './components/BottomSheet/BottomSheetHeader';
|
|
14
|
+
export { default as BottomSheetTitle } from './components/BottomSheet/BottomSheetTitle';
|
|
11
15
|
export { default as Chip } from './components/Chip';
|
|
12
16
|
export { Dialog, default as DialogDefault } from './components/Dialog';
|
|
13
17
|
export { ContactPopover, default as ContactPopoverDefault } from './components/ContactPopover';
|
|
@@ -41,11 +45,18 @@ export { default as AccordionExpandIcon } from './components/AccordionExpandIcon
|
|
|
41
45
|
export { default as VirtualizedTable } from './components/VirtualizedTable';
|
|
42
46
|
export { VirtuosoMockContext } from 'react-virtuoso';
|
|
43
47
|
export { useBreakpoints } from './hooks/useBreakpoints';
|
|
44
|
-
export
|
|
48
|
+
export { connectSpaceOverlay, computeOverlayRegion } from './components/SpaceOverlay/spaceOverlay';
|
|
49
|
+
export { OverlayPortal, SpaceOverlayProvider, overlayThemeOptions, useOverlayWindow } from './components/SpaceOverlay/SpaceOverlay';
|
|
50
|
+
export { overlayClipPath, overlayRegionMessage, parseOverlayRegion, parseOverlayRegionMessage } from './components/SpaceOverlay/region';
|
|
51
|
+
export { OverlayFrame } from './components/SpaceOverlay/OverlayFrame';
|
|
45
52
|
export type { TwakeTheme } from './lib/theme';
|
|
46
53
|
export type { PaletteJson } from './lib/types';
|
|
47
54
|
export type { AvatarProps } from './components/Avatar';
|
|
48
55
|
export type { ButtonProps } from './components/Button';
|
|
56
|
+
export type { BottomSheetProps, BottomSheetSettings, BottomSheetToolbarProps } from './components/BottomSheet/types';
|
|
57
|
+
export type { BottomSheetItemProps } from './components/BottomSheet/BottomSheetItem';
|
|
58
|
+
export type { BottomSheetHeaderProps } from './components/BottomSheet/BottomSheetHeader';
|
|
59
|
+
export type { BottomSheetTitleProps } from './components/BottomSheet/BottomSheetTitle';
|
|
49
60
|
export type { ContactPopoverProps } from './components/ContactPopover';
|
|
50
61
|
export { ContactPopoverActions, ContactPopoverChatAction, ContactPopoverVideoAction, ContactPopoverCalendarAction, ContactPopoverEmailAction } from './components/ContactPopover';
|
|
51
62
|
export type { ContactPopoverActionsProps } from './components/ContactPopover/ContactPopoverActions';
|
|
@@ -54,6 +65,10 @@ export type { ContactPopoverVideoActionProps } from './components/ContactPopover
|
|
|
54
65
|
export type { ContactPopoverCalendarActionProps } from './components/ContactPopover/ContactPopoverCalendarAction';
|
|
55
66
|
export type { ContactPopoverEmailActionProps } from './components/ContactPopover/ContactPopoverEmailAction';
|
|
56
67
|
export type { ChipProps } from './components/Chip';
|
|
68
|
+
export type { SpaceOverlay, SpaceOverlayStatus } from './components/SpaceOverlay/spaceOverlay';
|
|
69
|
+
export type { SpaceOverlayProviderProps } from './components/SpaceOverlay/SpaceOverlay';
|
|
70
|
+
export type { OverlayBox, OverlayRegion } from './components/SpaceOverlay/region';
|
|
71
|
+
export type { OverlayFrameProps } from './components/SpaceOverlay/OverlayFrame';
|
|
57
72
|
export type { DialogProps, DialogSize } from './components/Dialog';
|
|
58
73
|
export type { PointerAlertProps, PointerAlertDirection } from './components/PointerAlert';
|
|
59
74
|
export type { EmptyProps, EmptyIconSize, EmptyComponentsProps } from './components/Empty';
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,10 @@ export { radius } from './lib/radius';
|
|
|
11
11
|
export { TwakeMuiThemeProvider } from './components/ThemeProvider';
|
|
12
12
|
export { Avatar, default as AvatarDefault } from './components/Avatar';
|
|
13
13
|
export { Button, default as ButtonDefault } from './components/Button';
|
|
14
|
+
export { default as BottomSheet } from './components/BottomSheet';
|
|
15
|
+
export { default as BottomSheetItem } from './components/BottomSheet/BottomSheetItem';
|
|
16
|
+
export { default as BottomSheetHeader } from './components/BottomSheet/BottomSheetHeader';
|
|
17
|
+
export { default as BottomSheetTitle } from './components/BottomSheet/BottomSheetTitle';
|
|
14
18
|
export { default as Chip } from './components/Chip';
|
|
15
19
|
export { Dialog, default as DialogDefault } from './components/Dialog';
|
|
16
20
|
export { ContactPopover, default as ContactPopoverDefault } from './components/ContactPopover';
|
|
@@ -44,5 +48,8 @@ export { default as AccordionExpandIcon } from './components/AccordionExpandIcon
|
|
|
44
48
|
export { default as VirtualizedTable } from './components/VirtualizedTable';
|
|
45
49
|
export { VirtuosoMockContext } from 'react-virtuoso';
|
|
46
50
|
export { useBreakpoints } from './hooks/useBreakpoints';
|
|
47
|
-
export
|
|
51
|
+
export { connectSpaceOverlay, computeOverlayRegion } from './components/SpaceOverlay/spaceOverlay';
|
|
52
|
+
export { OverlayPortal, SpaceOverlayProvider, overlayThemeOptions, useOverlayWindow } from './components/SpaceOverlay/SpaceOverlay';
|
|
53
|
+
export { overlayClipPath, overlayRegionMessage, parseOverlayRegion, parseOverlayRegionMessage } from './components/SpaceOverlay/region';
|
|
54
|
+
export { OverlayFrame } from './components/SpaceOverlay/OverlayFrame';
|
|
48
55
|
export { ContactPopoverActions, ContactPopoverChatAction, ContactPopoverVideoAction, ContactPopoverCalendarAction, ContactPopoverEmailAction } from './components/ContactPopover';
|