Skip to content

Commit 90122d9

Browse files
authoredMar 22, 2023
[PAY-923] DMs: Add desktop entrypoints (#3083)
1 parent 00f27e8 commit 90122d9

File tree

16 files changed

+512
-290
lines changed

16 files changed

+512
-290
lines changed
 

‎packages/common/src/store/pages/chat/selectors.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { accountSelectors } from 'store/account'
55
import { cacheUsersSelectors } from 'store/cache'
66
import { CommonState } from 'store/reducers'
77
import { decodeHashId } from 'utils/hashIds'
8+
89
import { chatMessagesAdapter, chatsAdapter } from './slice'
910
const { getUserId } = accountSelectors
1011
const { getUsers } = cacheUsersSelectors

‎packages/stems/src/components/Popup/Popup.tsx

+169-81
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { getScrollParent } from 'utils/scrollParent'
1919
import { standard } from 'utils/transitions'
2020

2121
import styles from './Popup.module.css'
22-
import { PopupProps, Position, popupDefaultProps } from './types'
22+
import { PopupProps, Position, Origin, popupDefaultProps } from './types'
2323

2424
const messages = {
2525
close: 'close popup'
@@ -32,35 +32,82 @@ const messages = {
3232
const CONTAINER_INSET_PADDING = 16
3333

3434
/**
35-
* Gets the css transform origin prop from the display position
36-
* @param {Position} position
37-
* @returns {string} transform origin
35+
* Used to convert deprecated Position prop to transformOrigin prop
3836
*/
39-
const getTransformOrigin = (position: Position) =>
40-
({
41-
[Position.TOP_LEFT]: 'bottom right',
42-
[Position.TOP_CENTER]: 'bottom center',
43-
[Position.TOP_RIGHT]: 'bottom left',
44-
[Position.BOTTOM_LEFT]: 'top right',
45-
[Position.BOTTOM_CENTER]: 'top center',
46-
[Position.BOTTOM_RIGHT]: 'top left'
47-
}[position] ?? 'top center')
37+
const positionToTransformOriginMap: Record<Position, Origin> = {
38+
[Position.TOP_LEFT]: {
39+
horizontal: 'right',
40+
vertical: 'bottom'
41+
},
42+
[Position.TOP_CENTER]: {
43+
horizontal: 'center',
44+
vertical: 'bottom'
45+
},
46+
[Position.TOP_RIGHT]: {
47+
horizontal: 'left',
48+
vertical: 'bottom'
49+
},
50+
[Position.BOTTOM_LEFT]: {
51+
horizontal: 'right',
52+
vertical: 'top'
53+
},
54+
[Position.BOTTOM_CENTER]: {
55+
horizontal: 'center',
56+
vertical: 'top'
57+
},
58+
[Position.BOTTOM_RIGHT]: {
59+
horizontal: 'left',
60+
vertical: 'top'
61+
}
62+
}
63+
64+
/**
65+
* Used to convert deprecated Position prop to anchorOrigin prop
66+
*/
67+
const positionToAnchorOriginMap: Record<Position, Origin> = {
68+
[Position.TOP_LEFT]: {
69+
horizontal: 'left',
70+
vertical: 'top'
71+
},
72+
[Position.TOP_CENTER]: {
73+
horizontal: 'center',
74+
vertical: 'top'
75+
},
76+
[Position.TOP_RIGHT]: {
77+
horizontal: 'right',
78+
vertical: 'top'
79+
},
80+
[Position.BOTTOM_LEFT]: {
81+
horizontal: 'left',
82+
vertical: 'bottom'
83+
},
84+
[Position.BOTTOM_CENTER]: {
85+
horizontal: 'center',
86+
vertical: 'bottom'
87+
},
88+
[Position.BOTTOM_RIGHT]: {
89+
horizontal: 'right',
90+
vertical: 'bottom'
91+
}
92+
}
4893

4994
/**
5095
* Figures out whether the specified position would overflow the window
5196
* and picks a better position accordingly
52-
* @param {Position} position
53-
* @param {ClientRect} rect the content
54-
* @param {ClientRect} wrapper the wrapper of the content
55-
* @return {string | null} null if it would not overflow
97+
* @param {Origin} anchorOrigin where the origin is on the trigger
98+
* @param {Origin} transformOrigin where the origin is on the popup
99+
* @param {DOMRect} anchorRect the position and size of the trigger
100+
* @param {DOMRect} wrapperRect the position and size of the popup
101+
* @return {{ anchorOrigin: Origin, transformOrigin: Origin }} the new origin after accounting for overflow
56102
*/
57-
const getComputedPosition = (
58-
position: Position,
103+
const getComputedOrigins = (
104+
anchorOrigin: Origin,
105+
transformOrigin: Origin,
59106
anchorRect: DOMRect,
60107
wrapperRect: DOMRect,
61108
containerRef?: MutableRefObject<HTMLDivElement | undefined>
62-
): Position => {
63-
if (!anchorRect || !wrapperRect) return position
109+
) => {
110+
if (!anchorRect || !wrapperRect) return { anchorOrigin, transformOrigin }
64111

65112
let containerWidth, containerHeight
66113
if (containerRef && containerRef.current) {
@@ -75,24 +122,36 @@ const getComputedPosition = (
75122
containerHeight = window.innerHeight - CONTAINER_INSET_PADDING
76123
}
77124

78-
const overflowRight = anchorRect.x + wrapperRect.width > containerWidth
79-
const overflowLeft = anchorRect.x - wrapperRect.width < 0
80-
const overflowBottom = anchorRect.y + wrapperRect.height > containerHeight
81-
const overflowTop = anchorRect.y - wrapperRect.height < 0
125+
// Get new wrapper position
126+
const anchorTranslation = getOriginTranslation(anchorOrigin, anchorRect)
127+
const wrapperTranslation = getOriginTranslation(transformOrigin, wrapperRect)
128+
const wrapperX = anchorRect.x + anchorTranslation.x - wrapperTranslation.x
129+
const wrapperY = anchorRect.y + anchorTranslation.y - wrapperTranslation.y
130+
131+
// Check bounds of the wrapper in new position are inside container
132+
const overflowRight = wrapperX + wrapperRect.width > containerWidth
133+
const overflowLeft = wrapperX < 0
134+
const overflowBottom = wrapperY + wrapperRect.height > containerHeight
135+
const overflowTop = wrapperY < 0
82136

137+
// For all overflows, flip the position
83138
if (overflowRight) {
84-
position = position.replace('Right', 'Left') as Position
139+
anchorOrigin.horizontal = 'left'
140+
transformOrigin.horizontal = 'right'
85141
}
86142
if (overflowLeft) {
87-
position = position.replace('Left', 'Right') as Position
143+
anchorOrigin.horizontal = 'right'
144+
transformOrigin.horizontal = 'left'
88145
}
89146
if (overflowTop) {
90-
position = position.replace('top', 'bottom') as Position
147+
anchorOrigin.vertical = 'bottom'
148+
transformOrigin.vertical = 'top'
91149
}
92150
if (overflowBottom) {
93-
position = position.replace('bottom', 'top') as Position
151+
anchorOrigin.vertical = 'top'
152+
transformOrigin.vertical = 'bottom'
94153
}
95-
return position
154+
return { anchorOrigin, transformOrigin }
96155
}
97156

98157
/**
@@ -132,22 +191,63 @@ const getAdjustedPosition = (
132191
return adjusted
133192
}
134193

194+
/**
195+
* Gets the x, y offsets for the given origin using the dimensions
196+
* @param origin the relative origin
197+
* @param dimensions the dimensions to use with the relative origin
198+
* @returns the x and y coordinates of the new origin relative to the old one
199+
*/
200+
const getOriginTranslation = (
201+
origin: Origin,
202+
dimensions: { width: number; height: number }
203+
) => {
204+
let x = 0
205+
let y = 0
206+
const { width, height } = dimensions
207+
if (origin.horizontal === 'center') {
208+
x += width / 2
209+
} else if (origin.horizontal === 'right') {
210+
x += width
211+
}
212+
if (origin.vertical === 'center') {
213+
y += height / 2
214+
} else if (origin.vertical === 'bottom') {
215+
y += height
216+
}
217+
return { x, y }
218+
}
219+
220+
const defaultAnchorOrigin: Origin = {
221+
horizontal: 'center',
222+
vertical: 'bottom'
223+
}
224+
225+
const defaultTransformOrigin: Origin = {
226+
horizontal: 'center',
227+
vertical: 'top'
228+
}
229+
135230
/**
136231
* A popup is an in-place container that shows on top of the UI. A popup does
137232
* not impact the rest of the UI (e.g. graying it out). It differs
138233
* from modals, which do take over the whole UI and are usually
139234
* center-screened.
140235
*/
141236
export const Popup = forwardRef<HTMLDivElement, PopupProps>(function Popup(
142-
{
237+
props,
238+
ref
239+
) {
240+
const {
143241
anchorRef,
144-
animationDuration,
242+
animationDuration = 90,
145243
checkIfClickInside,
146244
children,
147245
isVisible,
148246
onAfterClose,
149247
onClose,
150-
position = Position.BOTTOM_CENTER,
248+
position,
249+
anchorOrigin: anchorOriginProp = defaultAnchorOrigin,
250+
transformOrigin: transformOriginProp = defaultTransformOrigin,
151251
hideCloseButton = false,
152252
showHeader,
153253
title,
@@ -156,9 +256,7 @@ export const Popup = forwardRef<HTMLDivElement, PopupProps>(function Popup(
156256
wrapperClassName,
157257
zIndex,
158258
containerRef
159-
},
160-
ref
161-
) {
259+
} = props
162260
const handleClose = useCallback(() => {
163261
onClose()
164262
setTimeout(() => {
@@ -168,6 +266,13 @@ export const Popup = forwardRef<HTMLDivElement, PopupProps>(function Popup(
168266
}, animationDuration)
169267
}, [onClose, onAfterClose, animationDuration])
170268

269+
const [anchorOrigin, transformOrigin] = position
270+
? [
271+
positionToAnchorOriginMap[position],
272+
positionToTransformOriginMap[position]
273+
]
274+
: [anchorOriginProp, transformOriginProp]
275+
171276
const popupRef: React.MutableRefObject<HTMLDivElement> = useClickOutside(
172277
handleClose,
173278
checkIfClickInside,
@@ -177,57 +282,41 @@ export const Popup = forwardRef<HTMLDivElement, PopupProps>(function Popup(
177282

178283
const wrapperRef = useRef<HTMLDivElement>(null)
179284
const originalTopPosition = useRef<number>(0)
180-
const [computedPosition, setComputedPosition] = useState(position)
181-
182-
const getRects = useCallback(
183-
() =>
184-
[anchorRef, wrapperRef].map((r) => r?.current?.getBoundingClientRect()),
185-
[anchorRef, wrapperRef]
186-
)
285+
const [computedTransformOrigin, setComputedTransformOrigin] =
286+
useState(anchorOrigin)
187287

188288
// On visible, set the position
189289
useEffect(() => {
190290
if (isVisible) {
191-
const [anchorRect, wrapperRect] = getRects()
291+
const [anchorRect, wrapperRect] = [anchorRef, wrapperRef].map((r) =>
292+
r?.current?.getBoundingClientRect()
293+
)
192294
if (!anchorRect || !wrapperRect) return
193295

194-
const computed = getComputedPosition(
195-
position,
296+
const {
297+
anchorOrigin: anchorOriginComputed,
298+
transformOrigin: transformOriginComputed
299+
} = getComputedOrigins(
300+
anchorOrigin,
301+
transformOrigin,
196302
anchorRect,
197303
wrapperRect,
198304
containerRef
199305
)
200-
setComputedPosition(computed)
201-
202-
const positionMap = {
203-
[Position.TOP_LEFT]: [
204-
anchorRect.y - wrapperRect.height,
205-
anchorRect.x - wrapperRect.width
206-
],
207-
[Position.TOP_CENTER]: [
208-
anchorRect.y - wrapperRect.height,
209-
anchorRect.x - wrapperRect.width / 2 + anchorRect.width / 2
210-
],
211-
[Position.TOP_RIGHT]: [
212-
anchorRect.y - wrapperRect.height,
213-
anchorRect.x + anchorRect.width
214-
],
215-
[Position.BOTTOM_LEFT]: [
216-
anchorRect.y + anchorRect.height,
217-
anchorRect.x - wrapperRect.width
218-
],
219-
[Position.BOTTOM_CENTER]: [
220-
anchorRect.y + anchorRect.height,
221-
anchorRect.x - wrapperRect.width / 2 + anchorRect.width / 2
222-
],
223-
[Position.BOTTOM_RIGHT]: [
224-
anchorRect.y + anchorRect.height,
225-
anchorRect.x + anchorRect.width
226-
]
227-
}
306+
setComputedTransformOrigin(transformOriginComputed)
307+
308+
const anchorTranslation = getOriginTranslation(
309+
anchorOriginComputed,
310+
anchorRect
311+
)
312+
const wrapperTranslation = getOriginTranslation(
313+
transformOriginComputed,
314+
wrapperRect
315+
)
316+
317+
const top = anchorRect.y + anchorTranslation.y - wrapperTranslation.y
318+
const left = anchorRect.x + anchorTranslation.x - wrapperTranslation.x
228319

229-
const [top, left] =
230-
positionMap[computed] ?? positionMap[Position.BOTTOM_CENTER]
231320
const { adjustedTop, adjustedLeft } = getAdjustedPosition(
232321
top,
233322
left,
@@ -242,13 +331,12 @@ export const Popup = forwardRef<HTMLDivElement, PopupProps>(function Popup(
242331
originalTopPosition.current = top
243332
}
244333
}, [
245-
position,
246334
isVisible,
247335
wrapperRef,
248336
anchorRef,
249-
computedPosition,
250-
setComputedPosition,
251-
getRects,
337+
anchorOrigin,
338+
transformOrigin,
339+
setComputedTransformOrigin,
252340
originalTopPosition,
253341
containerRef
254342
])
@@ -337,7 +425,7 @@ export const Popup = forwardRef<HTMLDivElement, PopupProps>(function Popup(
337425
key={key}
338426
style={{
339427
...props,
340-
transformOrigin: getTransformOrigin(computedPosition)
428+
transformOrigin: `${computedTransformOrigin.horizontal} ${computedTransformOrigin.vertical}`
341429
}}
342430
>
343431
{showHeader && (

0 commit comments

Comments
 (0)