[MM-56599] Upgrade to "@floating-ui/react" for advanced_text_editor/formatting_bar component (#25970)

This commit is contained in:
M-ZubairAhmed 2024-01-23 12:18:22 +00:00 committed by GitHub
parent 62064e3bf7
commit 797fe9b917
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 68 additions and 77 deletions

View File

@ -6,6 +6,7 @@
"version": "9.3.0",
"private": true,
"dependencies": {
"@floating-ui/react": "0.26.6",
"@floating-ui/react-dom": "1.0.0",
"@floating-ui/react-dom-interactions": "0.10.3",
"@giphy/js-fetch-api": "5.1.0",

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useFloating, offset} from '@floating-ui/react-dom';
import {useFloating, offset, useClick, useDismiss, useInteractions} from '@floating-ui/react';
import classNames from 'classnames';
import React, {memo, useCallback, useEffect, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
@ -13,7 +13,7 @@ import {DotsHorizontalIcon} from '@mattermost/compass-icons/components';
import type {ApplyMarkdownOptions} from 'utils/markdown/apply_markdown';
import FormattingIcon, {IconContainer} from './formatting_icon';
import {useFormattingBarControls, useGetLatest} from './hooks';
import {useFormattingBarControls} from './hooks';
export const Separator = styled.div`
display: block;
@ -147,38 +147,22 @@ const FormattingBar = (props: FormattingBarProps): JSX.Element => {
const {formatMessage} = useIntl();
const HiddenControlsButtonAriaLabel = formatMessage({id: 'accessibility.button.hidden_controls_button', defaultMessage: 'show hidden formatting options'});
const {x, y, reference, floating, strategy, update, refs: {reference: buttonRef, floating: floatingRef}} = useFloating<HTMLButtonElement>({
const {x, y, strategy, update, context, refs: {setReference, setFloating}} = useFloating<HTMLButtonElement>({
open: showHiddenControls,
onOpenChange: setShowHiddenControls,
placement: 'top',
middleware: [offset({mainAxis: 4})],
});
// this little helper hook always returns the latest refs and does not mess with the popper placement calculation
const getLatest = useGetLatest({
showHiddenControls,
buttonRef,
floatingRef,
});
const click = useClick(context);
const {getReferenceProps: getClickReferenceProps, getFloatingProps: getClickFloatingProps} = useInteractions([
click,
]);
useEffect(() => {
const handleClickOutside: EventListener = (event) => {
const {floatingRef, buttonRef} = getLatest();
const target = event.composedPath?.()?.[0] || event.target;
if (target instanceof Node) {
if (
floatingRef !== null &&
buttonRef !== null &&
!floatingRef.current?.contains(target) &&
!buttonRef.current?.contains(target)
) {
setShowHiddenControls(false);
}
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [getLatest, setShowHiddenControls]);
const dismiss = useDismiss(context);
const {getReferenceProps: getDismissReferenceProps, getFloatingProps: getDismissFloatingProps} = useInteractions([
dismiss,
]);
useEffect(() => {
update?.();
@ -186,13 +170,6 @@ const FormattingBar = (props: FormattingBarProps): JSX.Element => {
const hasHiddenControls = wideMode !== 'wide';
const toggleHiddenControls = useCallback((event?) => {
if (event) {
event.preventDefault();
}
setShowHiddenControls(!showHiddenControls);
}, [showHiddenControls]);
/**
* wrapping this factory in useCallback prevents it from constantly getting a new
* function signature as if we would define it directly in the props of
@ -222,9 +199,9 @@ const FormattingBar = (props: FormattingBarProps): JSX.Element => {
// if hidden controls are currently open close them
if (showHiddenControls) {
toggleHiddenControls();
setShowHiddenControls(true);
}
}, [getCurrentSelection, getCurrentMessage, applyMarkdown, showHiddenControls, toggleHiddenControls, disableControls]);
}, [getCurrentSelection, getCurrentMessage, applyMarkdown, showHiddenControls, disableControls]);
const leftPosition = wideMode === 'min' ? (x ?? 0) + DEFAULT_MIN_MODE_X_COORD : x ?? 0;
@ -263,10 +240,11 @@ const FormattingBar = (props: FormattingBarProps): JSX.Element => {
<>
<IconContainer
id={'HiddenControlsButton' + location}
ref={reference}
ref={setReference}
className={classNames({active: showHiddenControls})}
onClick={toggleHiddenControls}
aria-label={HiddenControlsButtonAriaLabel}
{...getClickReferenceProps()}
{...getDismissReferenceProps()}
>
<DotsHorizontalIcon
color={'currentColor'}
@ -282,8 +260,10 @@ const FormattingBar = (props: FormattingBarProps): JSX.Element => {
in={showHiddenControls}
>
<HiddenControlsContainer
ref={floating}
ref={setFloating}
style={hiddenControlsContainerStyles}
{...getClickFloatingProps()}
{...getDismissFloatingProps()}
>
{hiddenControls.map((mode) => {
return (

View File

@ -3,18 +3,13 @@
import type {Instance} from '@popperjs/core';
import {debounce} from 'lodash';
import React, {useCallback, useEffect, useLayoutEffect, useState} from 'react';
import type React from 'react';
import {useCallback, useEffect, useLayoutEffect, useState} from 'react';
import type {MarkdownMode} from 'utils/markdown/apply_markdown';
type WideMode = 'wide' | 'normal' | 'narrow' | 'min';
export function useGetLatest<T>(val: T) {
const ref = React.useRef<T>(val);
ref.current = val;
return React.useCallback(() => ref.current, []);
}
const useResponsiveFormattingBar = (ref: React.RefObject<HTMLDivElement>): WideMode => {
const [wideMode, setWideMode] = useState<WideMode>('wide');
const handleResize = useCallback(debounce(() => {

View File

@ -52,6 +52,7 @@
"name": "mattermost-webapp",
"version": "9.3.0",
"dependencies": {
"@floating-ui/react": "0.26.6",
"@floating-ui/react-dom": "1.0.0",
"@floating-ui/react-dom-interactions": "0.10.3",
"@giphy/js-fetch-api": "5.1.0",
@ -2362,18 +2363,34 @@
}
},
"node_modules/@floating-ui/core": {
"version": "1.5.0",
"license": "MIT",
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz",
"integrity": "sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==",
"dependencies": {
"@floating-ui/utils": "^0.1.3"
"@floating-ui/utils": "^0.2.0"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.5.3",
"license": "MIT",
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz",
"integrity": "sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==",
"dependencies": {
"@floating-ui/core": "^1.4.2",
"@floating-ui/utils": "^0.1.3"
"@floating-ui/core": "^1.5.3",
"@floating-ui/utils": "^0.2.0"
}
},
"node_modules/@floating-ui/react": {
"version": "0.26.6",
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.6.tgz",
"integrity": "sha512-FFDAuSlRwb8CY4/VvYio/wwk/0339B257yRpKwNOjcHWNYL/fgjl1KUvT3K6ZZ4WDbBWYc7Km4ITMuPZrS8omg==",
"dependencies": {
"@floating-ui/react-dom": "^2.0.6",
"@floating-ui/utils": "^0.2.1",
"tabbable": "^6.0.1"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/react-dom": {
@ -2399,9 +2416,22 @@
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/react/node_modules/@floating-ui/react-dom": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.6.tgz",
"integrity": "sha512-IB8aCRFxr8nFkdYZgH+Otd9EVQPJoynxeFRGTB8voPoZMRWo8XjYuCRgpI1btvuKY69XMiLnW+ym7zoBHM90Rw==",
"dependencies": {
"@floating-ui/dom": "^1.5.4"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.1.6",
"license": "MIT"
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.15.0",
@ -22320,6 +22350,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/tabbable": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
"integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
},
"node_modules/table": {
"version": "6.8.1",
"dev": true,

View File

@ -1,18 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import MuiFocusTrap, {FocusTrapProps as MuiFocusTrapProps} from '@mui/base/FocusTrap';
export interface Props {
active: MuiFocusTrapProps['open'];
children: MuiFocusTrapProps['children'];
}
export const FocusTrap = ({active, children}: Props) => {
return (
<MuiFocusTrap open={active}>
{children}
</MuiFocusTrap>
);
};

View File

@ -4,7 +4,6 @@
// type
export type {Props as GenericModalProps} from './generic_modal/generic_modal';
export type {CircleSkeletonLoaderProps, RectangleSkeletonLoaderProps} from './skeleton_loader';
export type {Props as FocusTrapProps} from './focus_trap';
export type {Props as PunchOutCoordsHeightAndWidth} from './common/hooks/useMeasurePunchouts';
// components
@ -14,7 +13,6 @@ export {CircleSkeletonLoader, RectangleSkeletonLoader} from './skeleton_loader';
export {TourTip} from './tour_tip/tour_tip';
export {TourTipBackdrop} from './tour_tip/tour_tip_backdrop';
export {PulsatingDot} from './pulsating_dot';
export {FocusTrap} from './focus_trap';
// hooks
export {useMeasurePunchouts} from './common/hooks/useMeasurePunchouts';