diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index 75bc3e7b2..d68a9d27c 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -116,7 +116,7 @@ "query-string": "^9.1.0", "react": "^18.0.0", "react-color": "^2.19.3", - "react-datepicker": "^4.8.0", + "react-datepicker": "^7.4.0", "react-dom": "^18.0.0", "react-error-boundary": "^4.0.11", "react-helmet": "^6.1.0", @@ -125,7 +125,6 @@ "react-inlinesvg": "^4.0.0", "react-intl": "^6.0.0", "react-motion": "^0.5.2", - "react-overlays": "^0.9.0", "react-popper": "^2.3.0", "react-redux": "^9.0.4", "react-router-dom": "^5.3.4", @@ -176,7 +175,6 @@ "@types/path-browserify": "^1.0.0", "@types/react": "^18.0.26", "@types/react-color": "^3.0.6", - "@types/react-datepicker": "^4.4.2", "@types/react-dom": "^18.0.10", "@types/react-helmet": "^6.1.5", "@types/react-motion": "^0.0.40", diff --git a/packages/pl-fe/src/components/ui/popover/popover.tsx b/packages/pl-fe/src/components/ui/popover/popover.tsx index 74c6400b7..f004f8a84 100644 --- a/packages/pl-fe/src/components/ui/popover/popover.tsx +++ b/packages/pl-fe/src/components/ui/popover/popover.tsx @@ -3,12 +3,14 @@ import { autoPlacement, FloatingArrow, offset, + shift, useClick, useDismiss, useFloating, useHover, useInteractions, useTransitionStyles, + type OffsetOptions, } from '@floating-ui/react'; import clsx from 'clsx'; import React, { useRef, useState } from 'react'; @@ -25,6 +27,7 @@ interface IPopover { interaction?: 'click' | 'hover'; /** Add a class to the reference (trigger) element */ referenceElementClassName?: string; + offsetOptions?: OffsetOptions; } /** @@ -33,14 +36,12 @@ interface IPopover { * Similar to tooltip, but requires a click and is used for larger blocks * of information. */ -const Popover: React.FC = (props) => { - const { children, content, referenceElementClassName, interaction = 'hover', isFlush = false } = props; - +const Popover: React.FC = ({ children, content, referenceElementClassName, interaction = 'hover', isFlush = false, offsetOptions = 10 }) => { const [isOpen, setIsOpen] = useState(false); const arrowRef = useRef(null); - const { x, y, strategy, refs, context } = useFloating({ + const { x, y, strategy, refs, context, placement } = useFloating({ open: isOpen, onOpenChange: setIsOpen, placement: 'top', @@ -48,7 +49,10 @@ const Popover: React.FC = (props) => { autoPlacement({ allowedPlacements: ['top', 'bottom'], }), - offset(10), + offset(offsetOptions), + shift({ + padding: 8, + }), arrow({ element: arrowRef, }), @@ -59,10 +63,11 @@ const Popover: React.FC = (props) => { initial: { opacity: 0, transform: 'scale(0.8)', + transformOrigin: placement === 'bottom' ? 'top' : 'bottom', }, duration: { - open: 200, - close: 200, + open: 100, + close: 100, }, }); @@ -82,6 +87,7 @@ const Popover: React.FC = (props) => { ref: refs.setReference, ...getReferenceProps(), className: clsx(children.props.className, referenceElementClassName), + 'aria-expanded': isOpen, })} {(isMounted) && ( @@ -94,20 +100,23 @@ const Popover: React.FC = (props) => { left: x ?? 0, ...styles, }} - className={ - clsx({ - 'z-40 rounded-lg bg-white shadow-2xl dark:bg-gray-900 dark:ring-2 dark:ring-primary-700': true, - 'p-6': !isFlush, - }) - } - {...getFloatingProps()} > - {content} +
+ {content} +
diff --git a/packages/pl-fe/src/features/pl-fe-config/components/color-picker.tsx b/packages/pl-fe/src/features/pl-fe-config/components/color-picker.tsx index 5e09b3ae3..87545c76a 100644 --- a/packages/pl-fe/src/features/pl-fe-config/components/color-picker.tsx +++ b/packages/pl-fe/src/features/pl-fe-config/components/color-picker.tsx @@ -1,49 +1,31 @@ -import { supportsPassiveEvents } from 'detect-passive-events'; -import React, { useEffect, useRef } from 'react'; -import { SketchPicker, ColorChangeHandler } from 'react-color'; +import React from 'react'; +import { SketchPicker, type ColorChangeHandler } from 'react-color'; -import { isMobile } from 'pl-fe/is-mobile'; - -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; +import { Popover } from 'pl-fe/components/ui'; interface IColorPicker { - style?: React.CSSProperties; value: string; onChange: ColorChangeHandler; - onClose: () => void; + className?: string; } -const ColorPicker: React.FC = ({ style, value, onClose, onChange }) => { - const node = useRef(null); - - const handleDocumentClick = (e: MouseEvent | TouchEvent) => { - if (node.current && !node.current.contains(e.target as HTMLElement)) { - onClose(); - } - }; - - useEffect(() => { - document.addEventListener('click', handleDocumentClick, false); - document.addEventListener('touchend', handleDocumentClick, listenerOptions); - - return () => { - document.removeEventListener('click', handleDocumentClick, false); - document.removeEventListener('touchend', handleDocumentClick); - }; - }, []); - - const pickerStyle: React.CSSProperties = { - ...style, - marginLeft: isMobile(window.innerWidth) ? '20px' : '12px', - position: 'absolute', - zIndex: 1000, - }; - - return ( -
- -
- ); -}; +const ColorPicker: React.FC = ({ value, onChange, className }) => ( +
+ + } + isFlush + > +
+ +
+); export { ColorPicker as default }; diff --git a/packages/pl-fe/src/features/pl-fe-config/components/color-with-picker.tsx b/packages/pl-fe/src/features/pl-fe-config/components/color-with-picker.tsx deleted file mode 100644 index 1783f3a32..000000000 --- a/packages/pl-fe/src/features/pl-fe-config/components/color-with-picker.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useState, useRef } from 'react'; -// @ts-ignore: TODO: upgrade react-overlays. v3.1 and above have TS definitions -import Overlay from 'react-overlays/lib/Overlay'; - -import { isMobile } from 'pl-fe/is-mobile'; - -import ColorPicker from './color-picker'; - -import type { ColorChangeHandler } from 'react-color'; - -interface IColorWithPicker { - value: string; - onChange: ColorChangeHandler; - className?: string; -} - -const ColorWithPicker: React.FC = ({ value, onChange, className }) => { - const node = useRef(null); - const [active, setActive] = useState(false); - const [placement, setPlacement] = useState(null); - - const hidePicker = () => { - setActive(false); - }; - - const showPicker = () => { - setActive(true); - setPlacement(isMobile(window.innerWidth) ? 'bottom' : 'right'); - }; - - const onToggle: React.MouseEventHandler = (e) => { - if (active) { - hidePicker(); - } else { - showPicker(); - } - - e.stopPropagation(); - }; - - return ( -
-
- - - - -
- ); -}; - -export { ColorWithPicker as default }; diff --git a/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-dropdown.tsx b/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-dropdown.tsx index c63e078ed..e8b5c3c69 100644 --- a/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-dropdown.tsx +++ b/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-dropdown.tsx @@ -1,9 +1,8 @@ -import React, { useRef, useState } from 'react'; +import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -// @ts-ignore -import Overlay from 'react-overlays/lib/Overlay'; import ForkAwesomeIcon from 'pl-fe/components/fork-awesome-icon'; +import { Popover } from 'pl-fe/components/ui'; import IconPickerMenu from './icon-picker-menu'; @@ -19,65 +18,32 @@ interface IIconPickerDropdown { const IconPickerDropdown: React.FC = ({ value, onPickIcon }) => { const intl = useIntl(); - const [active, setActive] = useState(false); - const [placement, setPlacement] = useState<'bottom' | 'top'>(); - - const target = useRef(null); - - const onShowDropdown: React.KeyboardEventHandler = ({ target }) => { - setActive(true); - - const { top } = (target as any).getBoundingClientRect(); - setPlacement(top * 2 < innerHeight ? 'bottom' : 'top'); - }; - - const onHideDropdown = () => { - setActive(false); - }; - - const onToggle: React.KeyboardEventHandler = (e) => { - e.stopPropagation(); - if (!e.key || e.key === 'Enter') { - if (active) { - onHideDropdown(); - } else { - onShowDropdown(e); - } - } - }; - - const handleKeyDown: React.KeyboardEventHandler = (e) => { - if (e.key === 'Escape') { - onHideDropdown(); - } - }; - const title = intl.formatMessage(messages.emoji); const forkAwesomeIcons = require('../forkawesome.json'); return ( -
-
} - onKeyDown={onToggle} - tabIndex={0} +
+ + } + isFlush > - -
+
+ +
- - - +
); }; diff --git a/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-menu.tsx b/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-menu.tsx index f6c114548..bf3a6a4d0 100644 --- a/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-menu.tsx +++ b/packages/pl-fe/src/features/pl-fe-config/components/icon-picker-menu.tsx @@ -1,6 +1,5 @@ import clsx from 'clsx'; -import { supportsPassiveEvents } from 'detect-passive-events'; -import React, { useCallback, useEffect, useRef } from 'react'; +import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { Text } from 'pl-fe/components/ui'; @@ -9,40 +8,16 @@ const messages = defineMessages({ emoji: { id: 'icon_button.label', defaultMessage: 'Select icon' }, }); -const listenerOptions = supportsPassiveEvents ? { passive: true } : false; - interface IIconPickerMenu { icons: Record>; - onClose: () => void; onPick: (icon: string) => void; style?: React.CSSProperties; } -const IconPickerMenu: React.FC = ({ icons, onClose, onPick, style }) => { +const IconPickerMenu: React.FC = ({ icons, onPick, style }) => { const intl = useIntl(); - const node = useRef(null); - - const handleDocumentClick = useCallback((e: MouseEvent | TouchEvent) => { - if (node.current && !node.current.contains(e.target as Node)) { - onClose(); - } - }, []); - - useEffect(() => { - document.addEventListener('click', handleDocumentClick, false); - document.addEventListener('touchend', handleDocumentClick, listenerOptions); - - return () => { - document.removeEventListener('click', handleDocumentClick, false); - document.removeEventListener('touchend', handleDocumentClick, listenerOptions as any); - - }; - }, []); - const setRef = (c: HTMLDivElement) => { - node.current = c; - if (!c) return; // Nice and dirty hack to display the icons @@ -54,7 +29,6 @@ const IconPickerMenu: React.FC = ({ icons, onClose, onPick, sty }; const handleClick = (icon: string) => { - onClose(); onPick(icon); }; @@ -79,16 +53,14 @@ const IconPickerMenu: React.FC = ({ icons, onClose, onPick, sty return (
-
- -
    - {Object.values(icons).flat().map(icon => renderIcon(icon))} -
-
+ +
    + {Object.values(icons).flat().map(icon => renderIcon(icon))} +
); }; diff --git a/packages/pl-fe/src/features/theme-editor/components/color.tsx b/packages/pl-fe/src/features/theme-editor/components/color.tsx index 1e7dc61dc..2739f5bc5 100644 --- a/packages/pl-fe/src/features/theme-editor/components/color.tsx +++ b/packages/pl-fe/src/features/theme-editor/components/color.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import ColorWithPicker from 'pl-fe/features/pl-fe-config/components/color-with-picker'; +import ColorPicker from 'pl-fe/features/pl-fe-config/components/color-picker'; import type { ColorChangeHandler } from 'react-color'; @@ -17,7 +17,7 @@ const Color: React.FC = ({ color, onChange }) => { }; return ( - = ({ label, value, onChange }) => return ( -