= {
children: 'This is a TabsList.Item',
},
};
-
diff --git a/src/components/Navigation/TabsList/components/TabsItem/TabsItem.tsx b/src/components/Navigation/TabsList/components/TabsItem/TabsItem.tsx
index 098c0e4e..28f463d2 100644
--- a/src/components/Navigation/TabsList/components/TabsItem/TabsItem.tsx
+++ b/src/components/Navigation/TabsList/components/TabsItem/TabsItem.tsx
@@ -1,6 +1,6 @@
'use client';
-import { ButtonHTMLAttributes } from 'react';
+import type { ButtonHTMLAttributes } from 'react';
import styles from './TabsItem.module.css';
import { classNames } from 'helpers/classNames';
@@ -34,15 +34,11 @@ export const TabsItem = ({
className={classNames(
styles.wrapper,
selected && styles['wrapper--selected'],
- className,
+ className
)}
{...restProps}
>
-
- {children}
-
+
{children}
);
};
diff --git a/src/components/Overlays/Modal/Modal.module.css.d.ts b/src/components/Overlays/Modal/Modal.module.css.d.ts
new file mode 100644
index 00000000..70e65808
--- /dev/null
+++ b/src/components/Overlays/Modal/Modal.module.css.d.ts
@@ -0,0 +1,7 @@
+declare const styles: {
+ readonly "body": string;
+ readonly "header": string;
+ readonly "wrapper": string;
+};
+export = styles;
+
diff --git a/src/components/Overlays/Modal/Modal.stories.tsx b/src/components/Overlays/Modal/Modal.stories.tsx
index a4771af2..ff0b6518 100644
--- a/src/components/Overlays/Modal/Modal.stories.tsx
+++ b/src/components/Overlays/Modal/Modal.stories.tsx
@@ -1,16 +1,24 @@
import { useState } from 'react';
-import { Decorator, Meta, StoryObj } from '@storybook/react';
+import type { Decorator, Meta, StoryObj } from '@storybook/react';
import { Icon28Close } from 'icons/28/close';
import { hideControls } from 'storybook/controls';
import { Button, Placeholder } from 'components';
-import { Modal, ModalProps } from './Modal';
+import type { ModalProps } from './Modal';
+import { Modal } from './Modal';
const meta = {
title: 'Overlays/Modal',
component: Modal,
- argTypes: hideControls('header', 'trigger', 'children', 'overlayComponent', 'snapPoints', 'fadeFromIndex'),
+ argTypes: hideControls(
+ 'header',
+ 'trigger',
+ 'children',
+ 'overlayComponent',
+ 'snapPoints',
+ 'fadeFromIndex'
+ ),
} satisfies Meta
;
export default meta;
@@ -18,13 +26,15 @@ export default meta;
type Story = StoryObj;
const DecoratorFullScreen: Decorator = (StoryComponent) => (
-
+
);
@@ -40,7 +50,7 @@ export const Playground: Story = {
>

@@ -53,7 +63,14 @@ const PlaceholderForNestedModal = (props: ModalProps) => (
}
+ action={
+
+
+
+ }
/>
);
@@ -61,7 +78,10 @@ export const NestedModals: Story = {
args: Playground.args,
render: (args) => (
-
+
),
decorators: [DecoratorFullScreen],
@@ -71,7 +91,13 @@ export const WithCloseButton: Story = {
args: {
...Playground.args,
header: (
- }>
+
+
+
+ }
+ >
Only iOS header
),
@@ -114,7 +140,14 @@ export const Controlled: Story = {
setIsOpen(true)}>Open again}
+ action={
+
+ }
>
-
- Fetch data and close
-
- )} />
+
+ Fetch data and close
+
+ }
+ />
);
diff --git a/src/components/Overlays/Modal/Modal.tsx b/src/components/Overlays/Modal/Modal.tsx
index 6c38e8c8..e1b7c553 100644
--- a/src/components/Overlays/Modal/Modal.tsx
+++ b/src/components/Overlays/Modal/Modal.tsx
@@ -1,26 +1,26 @@
'use client';
-import {
- forwardRef,
+import type {
ForwardRefExoticComponent,
HTMLAttributes,
ReactNode,
RefAttributes,
- useEffect,
- useState,
} from 'react';
+import { forwardRef, useEffect, useState } from 'react';
import styles from './Modal.module.css';
import { classNames } from 'helpers/classNames';
import { useAppRootContext } from 'hooks/useAppRootContext';
-import { Drawer } from '@xelene/vaul-with-scroll-fix';
+import { Drawer } from 'vaul';
+import { VisuallyHidden } from 'components/Service/VisuallyHidden/VisuallyHidden';
import { ModalClose } from './components/ModalClose/ModalClose';
import { ModalHeader } from './components/ModalHeader/ModalHeader';
import { ModalOverlay } from './components/ModalOverlay/ModalOverlay';
-export interface ModalProps extends Omit, 'onAnimationEnd'> {
+export interface ModalProps
+ extends Omit, 'onAnimationEnd'> {
/** Controls the displayed state of the modal, enabling external management. */
open?: boolean;
/** Callback fired upon state change, facilitating open/close state synchronization. */
@@ -49,75 +49,87 @@ export interface ModalProps extends Omit, 'onAnim
dismissible?: boolean;
}
-type ModalWithComponents = ForwardRefExoticComponent> & {
+type ModalWithComponents = ForwardRefExoticComponent<
+ ModalProps & RefAttributes
+> & {
Header: typeof ModalHeader;
- Overlay: typeof Drawer.Overlay;
+ Overlay: typeof ModalOverlay;
Close: typeof ModalClose;
};
+const defaultOverlay = ;
+
/**
* Modal component, providing a flexible dialog framework with customizable content and interaction models.
* It leverages the Drawer component from 'vaul' for its base functionality, enhanced with additional properties
* and behaviors specific to modal dialogues, such as overlay management and nested modals.
*/
-export const Modal = forwardRef(({
- overlayComponent = ,
- open,
- onOpenChange,
- header,
- className,
- children,
- nested,
- trigger,
- closeThreshold,
- scrollLockTimeout,
- snapPoints,
- fadeFromIndex,
- modal,
- preventScrollRestoration,
- dismissible,
- ...restProps
-}, ref) => {
- const container = useAppRootContext();
- const [portal, setPortal] = useState(container.portalContainer?.current);
+export const Modal = forwardRef(
+ (
+ {
+ overlayComponent = defaultOverlay,
+ open,
+ onOpenChange,
+ header,
+ className,
+ children,
+ nested,
+ trigger,
+ closeThreshold,
+ scrollLockTimeout,
+ snapPoints,
+ fadeFromIndex,
+ modal,
+ preventScrollRestoration,
+ dismissible,
+ ...restProps
+ },
+ ref
+ ) => {
+ const container = useAppRootContext();
+ const [portal, setPortal] = useState(container.portalContainer?.current);
- // This is internal optimization for AppRoot
- // React sets ref to normal value only after the first render
- // If we will have this logic inside the AppRoot component, then all tree will be re-rendered
- useEffect(() => {
- setPortal(container.portalContainer?.current);
- }, [container.portalContainer]);
+ // This is internal optimization for AppRoot
+ // React sets ref to normal value only after the first render
+ // If we will have this logic inside the AppRoot component, then all tree will be re-rendered
+ useEffect(() => {
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
+ setPortal(container.portalContainer?.current);
+ }, [container.portalContainer]);
- const Component = nested ? Drawer.NestedRoot : Drawer.Root;
- return (
-
- {trigger && {trigger}}
-
- {overlayComponent}
-
- {header}
-
- {children}
-
-
-
-
- );
-}) as ModalWithComponents;
+ const Component = nested ? Drawer.NestedRoot : Drawer.Root;
+ return (
+
+ {trigger && {trigger}}
+
+ {overlayComponent}
+
+
+
+
+ {header}
+ {children}
+
+
+
+ );
+ }
+) as ModalWithComponents;
Modal.Header = ModalHeader;
Modal.Overlay = ModalOverlay;
diff --git a/src/components/Overlays/Modal/components/ModalClose/ModalClose.stories.tsx b/src/components/Overlays/Modal/components/ModalClose/ModalClose.stories.tsx
index 45a42a65..225cd519 100644
--- a/src/components/Overlays/Modal/components/ModalClose/ModalClose.stories.tsx
+++ b/src/components/Overlays/Modal/components/ModalClose/ModalClose.stories.tsx
@@ -1,7 +1,8 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Placeholder } from 'components';
-import { ModalClose, ModalCloseProps } from './ModalClose';
+import type { ModalCloseProps } from './ModalClose';
+import { ModalClose } from './ModalClose';
const meta = {
title: 'Overlays/Modal/Modal.Close',
@@ -12,8 +13,6 @@ export default meta;
export const Playground: StoryObj = {
render: () => (
-
+
),
};
diff --git a/src/components/Overlays/Modal/components/ModalClose/ModalClose.tsx b/src/components/Overlays/Modal/components/ModalClose/ModalClose.tsx
index 8f569ec6..6bac3665 100644
--- a/src/components/Overlays/Modal/components/ModalClose/ModalClose.tsx
+++ b/src/components/Overlays/Modal/components/ModalClose/ModalClose.tsx
@@ -1,11 +1,14 @@
-import { ReactNode } from 'react';
+import type { ReactNode } from 'react';
-import { Drawer } from '@xelene/vaul-with-scroll-fix';
+import { Drawer } from 'vaul';
export interface ModalCloseProps {
children?: ReactNode;
}
export const ModalClose = (props: ModalCloseProps) => (
-
+
);
diff --git a/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.module.css.d.ts b/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.module.css.d.ts
new file mode 100644
index 00000000..b0c157c0
--- /dev/null
+++ b/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.module.css.d.ts
@@ -0,0 +1,8 @@
+declare const styles: {
+ readonly "after": string;
+ readonly "before": string;
+ readonly "children": string;
+ readonly "wrapper": string;
+};
+export = styles;
+
diff --git a/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.stories.tsx b/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.stories.tsx
index 5c0d85e9..6fd69474 100644
--- a/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.stories.tsx
+++ b/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.stories.tsx
@@ -2,7 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react';
import { hideControls } from 'storybook/controls';
import { Placeholder } from 'components';
-import { ModalHeader, ModalHeaderProps } from './ModalHeader';
+import type { ModalHeaderProps } from './ModalHeader';
+import { ModalHeader } from './ModalHeader';
const meta = {
title: 'Overlays/Modal/Modal.Header',
diff --git a/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.tsx b/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.tsx
index 1e606615..edd0e793 100644
--- a/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.tsx
+++ b/src/components/Overlays/Modal/components/ModalHeader/ModalHeader.tsx
@@ -1,4 +1,5 @@
-import { forwardRef, HTMLAttributes, ReactNode } from 'react';
+import type { HTMLAttributes, ReactNode } from 'react';
+import { forwardRef } from 'react';
import styles from './ModalHeader.module.css';
import { classNames } from 'helpers/classNames';
@@ -13,28 +14,27 @@ export interface ModalHeaderProps extends HTMLAttributes {
after?: ReactNode;
}
-export const ModalHeader = forwardRef(({
- before,
- after,
- className,
- children,
- ...props
-}, ref) => {
- const platform = usePlatform();
+export const ModalHeader = forwardRef(
+ ({ before, after, className, children, ...props }, ref) => {
+ const platform = usePlatform();
- return (
-
-
- {before}
-
- {platform === 'ios' && {children}}
-
- {after}
-
-
- );
-});
+ return (
+
+ {before}
+ {platform === 'ios' && (
+
+ {children}
+
+ )}
+ {after}
+
+ );
+ }
+);
diff --git a/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.module.css.d.ts b/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.module.css.d.ts
new file mode 100644
index 00000000..b8932fc9
--- /dev/null
+++ b/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.module.css.d.ts
@@ -0,0 +1,5 @@
+declare const styles: {
+ readonly "wrapper": string;
+};
+export = styles;
+
diff --git a/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.tsx b/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.tsx
index d3f3b5d4..e8d463e2 100644
--- a/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.tsx
+++ b/src/components/Overlays/Modal/components/ModalOverlay/ModalOverlay.tsx
@@ -6,7 +6,7 @@ import { hexToRGB } from 'helpers/color';
import { getTelegramData } from 'helpers/telegram';
import { useAppRootContext } from 'hooks/useAppRootContext';
-import { Drawer } from '@xelene/vaul-with-scroll-fix';
+import { Drawer } from 'vaul';
export interface ModalOverlayProps {
className?: string;
@@ -15,30 +15,31 @@ export interface ModalOverlayProps {
const DEFAULT_LIGHT_OVERLAY_RGB = [255, 255, 255];
const DEFAULT_DARK_OVERLAY_RGB = [33, 33, 33];
-export const ModalOverlay = forwardRef(({
- className,
- ...props
-}, ref) => {
- const context = useAppRootContext();
-
- // We don't use getComputedStyle because overlay renders before the appearance is changing
- const [r, g, b] = useMemo(() => {
- const telegramData = getTelegramData();
- if (telegramData && telegramData.themeParams.bg_color) {
- return hexToRGB(telegramData.themeParams.bg_color);
- }
-
- return context.appearance === 'light' ? DEFAULT_LIGHT_OVERLAY_RGB : DEFAULT_DARK_OVERLAY_RGB;
- }, [context.appearance]);
-
- return (
-
- );
-});
+export const ModalOverlay = forwardRef(
+ ({ className, ...props }, ref) => {
+ const context = useAppRootContext();
+
+ // We don't use getComputedStyle because overlay renders before the appearance is changing
+ const [r, g, b] = useMemo(() => {
+ const telegramData = getTelegramData();
+ if (telegramData && telegramData.themeParams.bg_color) {
+ return hexToRGB(telegramData.themeParams.bg_color);
+ }
+
+ return context.appearance === 'light'
+ ? DEFAULT_LIGHT_OVERLAY_RGB
+ : DEFAULT_DARK_OVERLAY_RGB;
+ }, [context.appearance]);
+
+ return (
+
+ );
+ }
+);
diff --git a/src/components/Overlays/Popper/Popper.module.css.d.ts b/src/components/Overlays/Popper/Popper.module.css.d.ts
new file mode 100644
index 00000000..b8932fc9
--- /dev/null
+++ b/src/components/Overlays/Popper/Popper.module.css.d.ts
@@ -0,0 +1,5 @@
+declare const styles: {
+ readonly "wrapper": string;
+};
+export = styles;
+
diff --git a/src/components/Overlays/Popper/Popper.stories.tsx b/src/components/Overlays/Popper/Popper.stories.tsx
index 7daf57e0..18de95d0 100644
--- a/src/components/Overlays/Popper/Popper.stories.tsx
+++ b/src/components/Overlays/Popper/Popper.stories.tsx
@@ -1,4 +1,5 @@
-import { MouseEvent, useState } from 'react';
+import type { MouseEvent } from 'react';
+import { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { hideControls } from 'storybook/controls';
@@ -11,7 +12,13 @@ import { Popper } from './Popper';
const meta = {
title: 'Overlays/Popper',
component: Popper,
- argTypes: hideControls('ArrowIcon', 'targetRef', 'arrowProps', 'Component', 'customMiddlewares'),
+ argTypes: hideControls(
+ 'ArrowIcon',
+ 'targetRef',
+ 'arrowProps',
+ 'Component',
+ 'customMiddlewares'
+ ),
} satisfies Meta;
export default meta;
@@ -19,6 +26,12 @@ export default meta;
type Story = StoryObj;
export const Playground: Story = {
+ args: {
+ targetRef: {
+ getBoundingClientRect: () =>
+ DOMRect.fromRect({ x: 0, y: 0, width: 0, height: 0 }),
+ },
+ },
render: (args) => {
const [virtualElement, setVirtualElement] = useState(() =>
DOMRect.fromRect({
@@ -26,7 +39,7 @@ export const Playground: Story = {
y: -200,
width: 10,
height: 10,
- }),
+ })
);
const handleClick = (event: MouseEvent) => {
@@ -36,7 +49,7 @@ export const Playground: Story = {
y: event.clientY,
width,
height,
- }),
+ })
);
};
@@ -75,4 +88,3 @@ export const Playground: Story = {
);
},
};
-
diff --git a/src/components/Overlays/Popper/Popper.tsx b/src/components/Overlays/Popper/Popper.tsx
index 5bd74fad..0ef0198e 100644
--- a/src/components/Overlays/Popper/Popper.tsx
+++ b/src/components/Overlays/Popper/Popper.tsx
@@ -1,23 +1,36 @@
'use client';
-import { ElementType, forwardRef, HTMLAttributes, RefObject, useState } from 'react';
+import type { ElementType, HTMLAttributes, RefObject } from 'react';
+import { forwardRef, useState } from 'react';
import styles from './Popper.module.css';
import { classNames } from 'helpers/classNames';
import { multipleRef } from 'helpers/react/refs';
import { useEnhancedEffect } from 'hooks/useEnhancedEffect';
-import { useFloating, VirtualElement } from '@floating-ui/react-dom';
+import type { VirtualElement } from '@floating-ui/react-dom';
+import { useFloating } from '@floating-ui/react-dom';
import { RootRenderer } from 'components/Service/RootRenderer/RootRenderer';
-import { FloatingArrow, FloatingArrowProps } from './components/FloatingArrow/FloatingArrow';
-import { DEFAULT_ARROW_HEIGHT, DEFAULT_ARROW_PADDING, DefaultIcon } from './components/FloatingArrow/icons/arrow';
+import type { FloatingArrowProps } from './components/FloatingArrow/FloatingArrow';
+import { FloatingArrow } from './components/FloatingArrow/FloatingArrow';
+import {
+ DEFAULT_ARROW_HEIGHT,
+ DEFAULT_ARROW_PADDING,
+ DefaultIcon,
+} from './components/FloatingArrow/icons/arrow';
import { autoUpdateFloatingElement } from './helpers/autoUpdateFloatingElement';
-import { useFloatingMiddlewares, UseFloatingMiddlewaresOptions } from './hooks/useFloatingMiddlewares';
+import type { UseFloatingMiddlewaresOptions } from './hooks/useFloatingMiddlewares';
+import { useFloatingMiddlewares } from './hooks/useFloatingMiddlewares';
-export interface PopperProps extends Omit, HTMLAttributes {
+export interface PopperProps
+ extends Omit<
+ UseFloatingMiddlewaresOptions,
+ 'arrowHeight' | 'arrowPadding' | 'arrowRef'
+ >,
+ HTMLAttributes {
/** Reference to the target element or virtual element for precise positioning. */
- targetRef: RefObject | VirtualElement;
+ targetRef: RefObject | VirtualElement;
/** Configuration and customization options for the floating arrow component. */
arrowProps?: FloatingArrowProps & {
/** Optionally override the default arrow height. */
@@ -37,81 +50,86 @@ export interface PopperProps extends Omit(
+ (
+ {
+ // UseFloatingMiddlewaresOptions
+ placement = 'auto',
+ sameWidth,
+ offsetByMainAxis = 8,
+ offsetByCrossAxis = 0,
+ withArrow = true,
+ customMiddlewares,
- // UseFloatingProps
- autoUpdateOnTargetResize = false,
+ // UseFloatingProps
+ autoUpdateOnTargetResize = false,
- // ArrowProps
- arrowProps,
- ArrowIcon = DefaultIcon,
+ // ArrowProps
+ arrowProps,
+ ArrowIcon = DefaultIcon,
- Component = 'div',
- style,
- targetRef,
- className,
- children,
- ...restProps
-}: PopperProps, ref) => {
- const [arrowRef, setArrowRef] = useState(null);
+ Component = 'div',
+ style,
+ targetRef,
+ className,
+ children,
+ ...restProps
+ },
+ ref
+ ) => {
+ const [arrowRef, setArrowRef] = useState(null);
- const { strictPlacement, middlewares } = useFloatingMiddlewares({
- placement,
- sameWidth,
- withArrow,
- arrowRef,
- arrowHeight: arrowProps?.height || DEFAULT_ARROW_HEIGHT,
- arrowPadding: arrowProps?.padding || DEFAULT_ARROW_PADDING,
- offsetByMainAxis,
- offsetByCrossAxis,
- customMiddlewares,
- });
+ const { strictPlacement, middlewares } = useFloatingMiddlewares({
+ placement,
+ sameWidth,
+ withArrow,
+ arrowRef,
+ arrowHeight: arrowProps?.height || DEFAULT_ARROW_HEIGHT,
+ arrowPadding: arrowProps?.padding || DEFAULT_ARROW_PADDING,
+ offsetByMainAxis,
+ offsetByCrossAxis,
+ customMiddlewares,
+ });
- const {
- placement: resolvedPlacement,
- refs,
- middlewareData,
- floatingStyles,
- } = useFloating({
- placement: strictPlacement,
- middleware: middlewares,
- whileElementsMounted(...args) {
- return autoUpdateFloatingElement(...args, {
- elementResize: autoUpdateOnTargetResize,
- });
- },
- });
+ const {
+ placement: resolvedPlacement,
+ refs,
+ middlewareData,
+ floatingStyles,
+ } = useFloating({
+ placement: strictPlacement,
+ middleware: middlewares,
+ whileElementsMounted(...args) {
+ return autoUpdateFloatingElement(...args, {
+ elementResize: autoUpdateOnTargetResize,
+ });
+ },
+ });
- useEnhancedEffect(() => {
- refs.setReference('current' in targetRef ? targetRef.current : targetRef);
- }, [refs.setReference, targetRef]);
+ useEnhancedEffect(() => {
+ refs.setReference('current' in targetRef ? targetRef.current : targetRef);
+ }, [refs.setReference, targetRef]);
- return (
-
-
- {withArrow && (
-
- )}
- {children}
-
-
- );
-});
+ return (
+
+
+ {withArrow && (
+
+ )}
+ {children}
+
+
+ );
+ }
+);
diff --git a/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.module.css.d.ts b/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.module.css.d.ts
new file mode 100644
index 00000000..100f9db9
--- /dev/null
+++ b/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.module.css.d.ts
@@ -0,0 +1,9 @@
+declare const styles: {
+ readonly "icon": string;
+ readonly "wrapper": string;
+ readonly "wrapper--placement-bottom": string;
+ readonly "wrapper--placement-left": string;
+ readonly "wrapper--placement-right": string;
+};
+export = styles;
+
diff --git a/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.tsx b/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.tsx
index 316bae2c..8f9c048b 100644
--- a/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.tsx
+++ b/src/components/Overlays/Popper/components/FloatingArrow/FloatingArrow.tsx
@@ -1,11 +1,13 @@
-import { ComponentType, forwardRef, HTMLAttributes, SVGAttributes } from 'react';
+import type { ComponentType, HTMLAttributes, SVGAttributes } from 'react';
+import { forwardRef } from 'react';
import styles from './FloatingArrow.module.css';
import { classNames } from 'helpers/classNames';
-import { Placement } from '@floating-ui/react-dom';
+import type { Placement } from '@floating-ui/react-dom';
-import { Coords, getArrowPositionData } from './helpers/getArrowPositionData';
+import type { Coords } from './helpers/getArrowPositionData';
+import { getArrowPositionData } from './helpers/getArrowPositionData';
import { DefaultIcon } from './icons/arrow';
const placementStyles = {
@@ -32,38 +34,43 @@ export interface FloatingArrowProps extends HTMLAttributes {
* such as a tooltip to signify its association with a target element.
* Supports custom arrow icons and positioning adjustments.
*/
-export const FloatingArrow = forwardRef(({
- style,
- offset,
- isStaticOffset,
- coords,
- placement = 'bottom',
- Icon = DefaultIcon,
- className,
- ...restProps
-}, ref) => {
- const [arrowPlacement, arrowStyles] = getArrowPositionData(
- placement,
- coords,
- offset,
- isStaticOffset,
- );
+export const FloatingArrow = forwardRef(
+ (
+ {
+ style,
+ offset,
+ isStaticOffset,
+ coords,
+ placement = 'bottom',
+ Icon = DefaultIcon,
+ className,
+ ...restProps
+ },
+ ref
+ ) => {
+ const [arrowPlacement, arrowStyles] = getArrowPositionData(
+ placement,
+ coords,
+ offset,
+ isStaticOffset
+ );
- return (
-
-
-
- );
-});
+ return (
+
+
+
+ );
+ }
+);
diff --git a/src/components/Overlays/Popper/components/FloatingArrow/helpers/getArrowPositionData.ts b/src/components/Overlays/Popper/components/FloatingArrow/helpers/getArrowPositionData.ts
index 743dc5fc..18d95833 100644
--- a/src/components/Overlays/Popper/components/FloatingArrow/helpers/getArrowPositionData.ts
+++ b/src/components/Overlays/Popper/components/FloatingArrow/helpers/getArrowPositionData.ts
@@ -1,21 +1,25 @@
-import { CSSProperties } from 'react';
+import type { CSSProperties } from 'react';
-import { Placement } from '@floating-ui/react-dom';
+import type { Placement } from '@floating-ui/react-dom';
-export type Coords = {
+export interface Coords {
x?: number;
y?: number;
-};
+}
+
+const defaultCoords = { x: 0, y: 0 };
export const getArrowPositionData = (
placement: Placement,
- coords: Coords = { x: 0, y: 0 },
+ coords: Coords = defaultCoords,
offset = 0,
- isStaticOffset = false,
+ isStaticOffset = false
): [undefined | 'right' | 'bottom' | 'left', CSSProperties] => {
const withOffset = (isVerticalPlacement: boolean) => {
const parsedCoords = { x: coords.x || 0, y: coords.y || 0 };
- return isStaticOffset ? offset : parsedCoords[isVerticalPlacement ? 'y' : 'x'] + offset;
+ return isStaticOffset
+ ? offset
+ : parsedCoords[isVerticalPlacement ? 'y' : 'x'] + offset;
};
if (placement.startsWith('top')) {
@@ -55,5 +59,4 @@ export const getArrowPositionData = (
right: 0,
},
];
-
};
diff --git a/src/components/Overlays/Popper/components/FloatingArrow/icons/arrow.tsx b/src/components/Overlays/Popper/components/FloatingArrow/icons/arrow.tsx
index 11eaba04..36e4cd58 100644
--- a/src/components/Overlays/Popper/components/FloatingArrow/icons/arrow.tsx
+++ b/src/components/Overlays/Popper/components/FloatingArrow/icons/arrow.tsx
@@ -1,4 +1,4 @@
-import { SVGAttributes } from 'react';
+import type { SVGAttributes } from 'react';
export const DEFAULT_ARROW_WIDTH = 22;
export const DEFAULT_ARROW_HEIGHT = 6;
@@ -15,7 +15,9 @@ export const DefaultIcon = (props: SVGAttributes) => (
xmlns="http://www.w3.org/2000/svg"
{...props}
>
-
+
);
-
diff --git a/src/components/Overlays/Popper/helpers/autoUpdateFloatingElement.ts b/src/components/Overlays/Popper/helpers/autoUpdateFloatingElement.ts
index 55051e39..fb18e9a1 100644
--- a/src/components/Overlays/Popper/helpers/autoUpdateFloatingElement.ts
+++ b/src/components/Overlays/Popper/helpers/autoUpdateFloatingElement.ts
@@ -1,6 +1,11 @@
-import { FloatingElement } from '@floating-ui/core';
-import { autoUpdate, AutoUpdateOptions, ReferenceType } from '@floating-ui/react-dom';
-import { isHTMLElement } from '@floating-ui/utils/dom';
+import { isHTMLElement } from 'helpers/dom';
+
+import type {
+ AutoUpdateOptions,
+ FloatingElement,
+ ReferenceType,
+} from '@floating-ui/react-dom';
+import { autoUpdate } from '@floating-ui/react-dom';
const defaultOptions = {
ancestorScroll: true,
@@ -13,7 +18,7 @@ export const autoUpdateFloatingElement = (
reference: ReferenceType,
floating: FloatingElement,
update: () => void,
- options: Partial = defaultOptions,
+ options: Partial = defaultOptions
): ReturnType => {
const { elementResize = false, ...restOptions } = options;
diff --git a/src/components/Overlays/Popper/hooks/helpers/alignment.ts b/src/components/Overlays/Popper/hooks/helpers/alignment.ts
index 9d103d9e..b5fd983d 100644
--- a/src/components/Overlays/Popper/hooks/helpers/alignment.ts
+++ b/src/components/Overlays/Popper/hooks/helpers/alignment.ts
@@ -1,12 +1,16 @@
-import { Placement } from '@floating-ui/react-dom';
+import type { Placement } from '@floating-ui/react-dom';
-import { AutoPlacementType, PlacementWithAuto } from '../types';
+import type { AutoPlacementType, PlacementWithAuto } from '../types';
-export const isNotAutoPlacement = (placement: PlacementWithAuto): placement is Placement => {
+export const isNotAutoPlacement = (
+ placement: PlacementWithAuto
+): placement is Placement => {
return !placement.startsWith('auto');
};
-export const getAutoPlacementAlignment = (placement: AutoPlacementType): 'start' | 'end' | null => {
+export const getAutoPlacementAlignment = (
+ placement: AutoPlacementType
+): 'start' | 'end' | null => {
const align = placement.replace(/auto-|auto/, '');
return align === 'start' || align === 'end' ? align : null;
};
diff --git a/src/components/Overlays/Popper/hooks/useFloatingMiddlewares.ts b/src/components/Overlays/Popper/hooks/useFloatingMiddlewares.ts
index 2aca3985..70458c30 100644
--- a/src/components/Overlays/Popper/hooks/useFloatingMiddlewares.ts
+++ b/src/components/Overlays/Popper/hooks/useFloatingMiddlewares.ts
@@ -1,18 +1,20 @@
import { useMemo } from 'react';
+import type { ArrowOptions, Middleware } from '@floating-ui/react-dom';
import {
arrow,
- ArrowOptions,
autoPlacement,
flip,
- Middleware,
offset,
shift,
size,
} from '@floating-ui/react-dom';
-import { getAutoPlacementAlignment, isNotAutoPlacement } from './helpers/alignment';
-import { PlacementWithAuto } from './types';
+import {
+ getAutoPlacementAlignment,
+ isNotAutoPlacement,
+} from './helpers/alignment';
+import type { PlacementWithAuto } from './types';
export interface UseFloatingMiddlewaresOptions {
/** By default, the component will automatically choose the best placement */
@@ -51,7 +53,10 @@ export const useFloatingMiddlewares = ({
const middlewares: Middleware[] = [
offset({
crossAxis: offsetByCrossAxis,
- mainAxis: withArrow && arrowHeight ? offsetByMainAxis + arrowHeight : offsetByMainAxis,
+ mainAxis:
+ withArrow && arrowHeight
+ ? offsetByMainAxis + arrowHeight
+ : offsetByMainAxis,
}),
];
@@ -59,12 +64,14 @@ export const useFloatingMiddlewares = ({
middlewares.push(
flip({
fallbackAxisSideDirection: 'start',
- }),
+ })
);
} else {
- middlewares.push(autoPlacement({
- alignment: getAutoPlacementAlignment(placement),
- }));
+ middlewares.push(
+ autoPlacement({
+ alignment: getAutoPlacementAlignment(placement),
+ })
+ );
}
middlewares.push(shift());
@@ -77,7 +84,7 @@ export const useFloatingMiddlewares = ({
width: `${rects.reference.width}px`,
});
},
- }),
+ })
);
}
@@ -90,7 +97,7 @@ export const useFloatingMiddlewares = ({
arrow({
element: arrowRef,
padding: arrowPadding,
- }),
+ })
);
}
diff --git a/src/components/Overlays/Tooltip/Tooltip.module.css.d.ts b/src/components/Overlays/Tooltip/Tooltip.module.css.d.ts
new file mode 100644
index 00000000..01caf410
--- /dev/null
+++ b/src/components/Overlays/Tooltip/Tooltip.module.css.d.ts
@@ -0,0 +1,8 @@
+declare const styles: {
+ readonly "arrow": string;
+ readonly "wrapper": string;
+ readonly "wrapper--dark": string;
+ readonly "wrapper--ios": string;
+};
+export = styles;
+
diff --git a/src/components/Overlays/Tooltip/Tooltip.stories.tsx b/src/components/Overlays/Tooltip/Tooltip.stories.tsx
index eb65ff67..de478fb5 100644
--- a/src/components/Overlays/Tooltip/Tooltip.stories.tsx
+++ b/src/components/Overlays/Tooltip/Tooltip.stories.tsx
@@ -9,7 +9,13 @@ import { Tooltip } from './Tooltip';
const meta = {
title: 'Overlays/Tooltip',
component: Tooltip,
- argTypes: hideControls('ArrowIcon', 'targetRef', 'arrowProps', 'Component', 'customMiddlewares'),
+ argTypes: hideControls(
+ 'ArrowIcon',
+ 'targetRef',
+ 'arrowProps',
+ 'Component',
+ 'customMiddlewares'
+ ),
parameters: {
layout: 'centered',
},
@@ -32,27 +38,37 @@ export const Playground: Story = {
return (
<>
-