From bc3f02aee6d1a4c6415f3cd0e5c2ae6e1da9db7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 27 Sep 2024 20:35:59 +0200 Subject: [PATCH 1/3] pl-fe: replace react-overlays with @floating-ui/react MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- packages/pl-fe/package.json | 4 +- .../src/components/ui/popover/popover.tsx | 41 +++++--- .../pl-fe-config/components/color-picker.tsx | 62 +++++------- .../components/color-with-picker.tsx | 59 ----------- .../components/icon-picker-dropdown.tsx | 78 +++++---------- .../components/icon-picker-menu.tsx | 44 ++------- .../theme-editor/components/color.tsx | 4 +- .../pl-fe/src/features/theme-editor/index.tsx | 4 +- packages/pl-fe/yarn.lock | 98 +++++-------------- 9 files changed, 105 insertions(+), 289 deletions(-) delete mode 100644 packages/pl-fe/src/features/pl-fe-config/components/color-with-picker.tsx 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 ( - Date: Fri, 27 Sep 2024 20:37:27 +0200 Subject: [PATCH 2/3] pl-fe: update lodash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- packages/pl-fe/package.json | 2 +- packages/pl-fe/yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index d68a9d27c..a0915fb82 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -104,7 +104,7 @@ "lexical": "^0.18.0", "line-awesome": "^1.3.0", "localforage": "^1.10.0", - "lodash": "^4.7.11", + "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.6.0", "multiselect-react-dropdown": "^2.0.25", "path-browserify": "^1.0.1", diff --git a/packages/pl-fe/yarn.lock b/packages/pl-fe/yarn.lock index 2502c4c96..04da86bef 100644 --- a/packages/pl-fe/yarn.lock +++ b/packages/pl-fe/yarn.lock @@ -8040,7 +8040,7 @@ lodash@4.17.5: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" integrity sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw== -lodash@^4.0.1, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.11: +lodash@^4.0.1, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== From 4378adef9aa3477a6d4370128dc87c03a039275c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 27 Sep 2024 20:38:18 +0200 Subject: [PATCH 3/3] pl-fe: update types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- packages/pl-fe/package.json | 2 +- packages/pl-fe/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index a0915fb82..5217a5857 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -170,7 +170,7 @@ "@types/escape-html": "^1.0.1", "@types/http-link-header": "^1.0.3", "@types/leaflet": "^1.8.0", - "@types/lodash": "^4.14.180", + "@types/lodash": "^4.17.9", "@types/object-assign": "^4.0.30", "@types/path-browserify": "^1.0.0", "@types/react": "^18.0.26", diff --git a/packages/pl-fe/yarn.lock b/packages/pl-fe/yarn.lock index 04da86bef..bb559e359 100644 --- a/packages/pl-fe/yarn.lock +++ b/packages/pl-fe/yarn.lock @@ -2849,10 +2849,10 @@ dependencies: "@types/geojson" "*" -"@types/lodash@^4.14.180": - version "4.14.180" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" - integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== +"@types/lodash@^4.17.9": + version "4.17.9" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.9.tgz#0dc4902c229f6b8e2ac5456522104d7b1a230290" + integrity sha512-w9iWudx1XWOHW5lQRS9iKpK/XuRhnN+0T7HvdCCd802FYkT1AMTnxndJHGrNJwRoRHkslGr4S29tjm1cT7x/7w== "@types/mdast@^4.0.0": version "4.0.4"