+type AsyncComponent = () => Promise<{ default: React.ComponentType
}>
+
+const withHoc =
(asyncComponent: AsyncComponent
, hoc: HOC
) => {
+ return async () => {
+ const { default: component } = await asyncComponent();
+ return { default: hoc(component) };
+ };
+};
+
+export default withHoc;
\ No newline at end of file
diff --git a/app/soapbox/components/profile-hover-card.tsx b/app/soapbox/components/profile-hover-card.tsx
index 2dd6a3fdd..d0c8a93d4 100644
--- a/app/soapbox/components/profile-hover-card.tsx
+++ b/app/soapbox/components/profile-hover-card.tsx
@@ -106,7 +106,7 @@ export const ProfileHoverCard: React.FC = ({ visible = true }
onMouseEnter={handleMouseEnter(dispatch)}
onMouseLeave={handleMouseLeave(dispatch)}
>
-
+
diff --git a/app/soapbox/components/status-media.tsx b/app/soapbox/components/status-media.tsx
index 0867e6ca2..192a4c169 100644
--- a/app/soapbox/components/status-media.tsx
+++ b/app/soapbox/components/status-media.tsx
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { openModal } from 'soapbox/actions/modals';
import AttachmentThumbs from 'soapbox/components/attachment-thumbs';
+import { GroupLinkPreview } from 'soapbox/features/groups/components/group-link-preview';
import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder-card';
import Card from 'soapbox/features/status/components/card';
import Bundle from 'soapbox/features/ui/components/bundle';
@@ -153,6 +154,10 @@ const StatusMedia: React.FC = ({
);
}
+ } else if (status.spoiler_text.length === 0 && !status.quote && status.card?.group) {
+ media = (
+
+ );
} else if (status.spoiler_text.length === 0 && !status.quote && status.card) {
media = (
= (props) => {
return (
}
text={
diff --git a/app/soapbox/components/ui/tooltip/tooltip.css b/app/soapbox/components/ui/tooltip/tooltip.css
deleted file mode 100644
index 670571c8b..000000000
--- a/app/soapbox/components/ui/tooltip/tooltip.css
+++ /dev/null
@@ -1,12 +0,0 @@
-:root {
- --reach-tooltip: 1;
-}
-
-[data-reach-tooltip] {
- @apply pointer-events-none absolute px-2.5 py-1.5 rounded shadow whitespace-nowrap text-xs font-medium bg-gray-800 text-gray-100 dark:bg-gray-100 dark:text-gray-900;
- z-index: 100;
-}
-
-[data-reach-tooltip-arrow] {
- @apply absolute z-50 w-0 h-0 border-l-8 border-solid border-l-transparent border-r-8 border-r-transparent border-b-8 border-b-gray-800 dark:border-b-gray-100;
-}
diff --git a/app/soapbox/components/ui/tooltip/tooltip.tsx b/app/soapbox/components/ui/tooltip/tooltip.tsx
index f2b6ffedc..0d492bcb6 100644
--- a/app/soapbox/components/ui/tooltip/tooltip.tsx
+++ b/app/soapbox/components/ui/tooltip/tooltip.tsx
@@ -1,67 +1,88 @@
-import { TooltipPopup, useTooltip } from '@reach/tooltip';
-import React from 'react';
-
-import Portal from '../portal/portal';
-
-import './tooltip.css';
+import {
+ arrow,
+ FloatingArrow,
+ FloatingPortal,
+ offset,
+ useFloating,
+ useHover,
+ useInteractions,
+ useTransitionStyles,
+} from '@floating-ui/react';
+import React, { useRef, useState } from 'react';
interface ITooltip {
+ /** Element to display the tooltip around. */
+ children: React.ReactElement>
/** Text to display in the tooltip. */
text: string
- /** Element to display the tooltip around. */
- children: React.ReactNode
}
-const centered = (triggerRect: any, tooltipRect: any) => {
- const triggerCenter = triggerRect.left + triggerRect.width / 2;
- const left = triggerCenter - tooltipRect.width / 2;
- const maxLeft = window.innerWidth - tooltipRect.width - 2;
- return {
- left: Math.min(Math.max(2, left), maxLeft) + window.scrollX,
- top: triggerRect.bottom + 8 + window.scrollY,
- };
-};
+/**
+ * Tooltip
+ */
+const Tooltip: React.FC = (props) => {
+ const { children, text } = props;
-/** Hoverable tooltip element. */
-const Tooltip: React.FC = ({
- children,
- text,
-}) => {
- // get the props from useTooltip
- const [trigger, tooltip] = useTooltip();
+ const [isOpen, setIsOpen] = useState(false);
- // destructure off what we need to position the triangle
- const { isVisible, triggerRect } = tooltip;
+ const arrowRef = useRef(null);
+
+ const { x, y, strategy, refs, context } = useFloating({
+ open: isOpen,
+ onOpenChange: setIsOpen,
+ placement: 'top',
+ middleware: [
+ offset(6),
+ arrow({
+ element: arrowRef,
+ }),
+ ],
+ });
+
+ const hover = useHover(context);
+ const { isMounted, styles } = useTransitionStyles(context, {
+ initial: {
+ opacity: 0,
+ transform: 'scale(0.8)',
+ },
+ duration: {
+ open: 200,
+ close: 200,
+ },
+ });
+
+ const { getReferenceProps, getFloatingProps } = useInteractions([
+ hover,
+ ]);
return (
-
- {React.cloneElement(children as any, trigger)}
+ <>
+ {React.cloneElement(children, {
+ ref: refs.setReference,
+ ...getReferenceProps(),
+ })}
- {isVisible && (
- // The Triangle. We position it relative to the trigger, not the popup
- // so that collisions don't have a triangle pointing off to nowhere.
- // Using a Portal may seem a little extreme, but we can keep the
- // positioning logic simpler here instead of needing to consider
- // the popup's position relative to the trigger and collisions
-
+ {(isMounted) && (
+
-
+ className='pointer-events-none z-[100] whitespace-nowrap rounded bg-gray-800 px-2.5 py-1.5 text-xs font-medium text-gray-100 shadow dark:bg-gray-100 dark:text-gray-900'
+ {...getFloatingProps()}
+ >
+ {text}
+
+
+