Skip to content

Commit b4d40ba

Browse files
author
Kubit
committed
Add drag element and improve modal component
1 parent 30fa282 commit b4d40ba

15 files changed

+162
-54
lines changed

src/components/modal/modal.styled.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,7 @@ export const ModalContentStyled = styled.div<IModalStyled & ModalShowScrollType>
105105
export const ModalFooterStyled = styled.div<IModalStyled>`
106106
${props => getStyles(props.$styles.footer)}
107107
`;
108+
109+
export const DraggableIcon = styled.div<IModalStyled>`
110+
${props => getStyles(props.$styles?.dragIconContainer)}
111+
`;

src/components/modal/modalControlled.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22

33
import { STYLES_NAME } from '@/constants';
4-
import { useMediaDevice, useScrollEffect, useStyles, useZoomEffect } from '@/hooks';
4+
import { useMediaDevice, useScrollEffect, useStyles, useSwipeDown, useZoomEffect } from '@/hooks';
55
import { ErrorBoundary } from '@/provider/errorBoundary/errorBoundary';
66
import { FallbackComponent } from '@/provider/errorBoundary/fallbackComponent';
77
import { DeviceBreakpointsType } from '@/types';
@@ -40,17 +40,29 @@ const ModalControlledComponent = React.forwardRef(
4040
const zoomRef = useZoomEffect(CONTAINER_STYLES_EDIT, MAX_ZOOM);
4141
const zoomRefChild = useZoomEffect(CONTENT_STYLES_EDIT, MAX_ZOOM);
4242

43+
const { setPopoverRef, setDragIconRef } = useSwipeDown(props.popover?.animationOptions, () =>
44+
props.onClose?.()
45+
);
46+
47+
const handlePopoverCloseInternally = () => {
48+
props.popover?.onCloseInternally?.();
49+
props.onClose?.();
50+
};
51+
4352
const modalStructure = (
4453
<ModalStandAlone
4554
{...props}
4655
ref={ref}
4756
device={device}
57+
dragIconRef={setDragIconRef}
58+
popover={{ ...props.popover, forwardedRef: setPopoverRef }}
4859
resizeRef={resizeRef}
4960
scrollableRef={scrollableRef}
5061
shadowRef={shadowRef}
5162
styles={styles}
5263
zoomRef={zoomRef}
5364
zoomRefChild={zoomRefChild}
65+
onPopoverCloseInternally={handlePopoverCloseInternally}
5466
/>
5567
);
5668

src/components/modal/modalStandAlone.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { useId } from '@/hooks';
1212
import { DeviceBreakpointsType } from '@/types';
1313

1414
import {
15+
DraggableIcon,
1516
ModalCloseButtonStyled,
1617
ModalContentStyled,
1718
ModalFooterStyled,
@@ -95,6 +96,7 @@ const ModalStandAloneComponent = (
9596
positionVariant={PopoverPositionVariantType.FIXED}
9697
trapFocusInsideModal={true}
9798
variant={props.styles.popoverVariant}
99+
onCloseInternally={props.onPopoverCloseInternally}
98100
{...props.popover}
99101
>
100102
<ModalStyled
@@ -109,6 +111,11 @@ const ModalStandAloneComponent = (
109111
onKeyDown={event => props.onKeyDown?.(event)}
110112
>
111113
<ModalHeaderStyled ref={shadowRef} $styles={props.styles}>
114+
{!props.blocked && props.dragIcon && (
115+
<DraggableIcon ref={props.dragIconRef} $styles={props.styles}>
116+
<ElementOrIcon customIconStyles={props.styles?.dragIcon} {...props.dragIcon} />
117+
</DraggableIcon>
118+
)}
112119
{!props.blocked && props.closeIcon?.icon && (
113120
<ModalCloseButtonStyled $styles={props.styles}>
114121
<ElementOrIcon

src/components/modal/modalUnControlled.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const ModalUnControlledComponent = <V extends string | unknown>(
5151
open={open}
5252
popover={{ ...popover, onCloseInternally: handlePopoverCloseInternally }}
5353
variant={variant}
54+
onClose={onClose}
5455
onKeyDown={onKeyDown}
5556
/>
5657
);

src/components/modal/stories/modal.stories.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ const StoryWithHooks = args => {
4444
return (
4545
<div style={{ width: 'fit-content' }}>
4646
<button onClick={handleOpen}>Open Modal</button>
47-
<Story {...args} closeIcon={{ ...args.closeIcon, onClick: handleClose }} open={open} />
47+
<Story
48+
{...args}
49+
closeIcon={{ ...args.closeIcon, onClick: handleClose }}
50+
open={open}
51+
onClose={handleClose}
52+
/>
4853
</div>
4954
);
5055
};

src/components/modal/types/modal.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ export interface IModalStandAlone {
5757
device: DeviceBreakpointsType;
5858
dataTestId?: string;
5959
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
60+
onPopoverCloseInternally?: () => void;
61+
dragIcon?: IElementOrIcon;
62+
dragIconRef?: (node) => void;
6063
/* To useScrollableEffect */
6164
scrollableRef: (node) => void;
6265
resizeRef: (node) => void;
@@ -73,16 +76,16 @@ type OmitProps =
7376
| 'resizeRef'
7477
| 'shadowRef'
7578
| 'zoomRef'
76-
| 'zoomRefChild';
79+
| 'zoomRefChild'
80+
| 'dragIconRef';
7781

7882
export interface IModalControlled<V = undefined extends string ? unknown : string>
7983
extends Omit<IModalStandAlone, OmitProps>,
8084
Omit<CustomTokenTypes<ModalBaseStylesType>, 'cts' | 'extraCt'> {
8185
variant: V;
8286
portalId?: string;
87+
onClose?: () => void;
8388
}
8489

8590
export interface IModalUnControlled<V = undefined extends string ? unknown : string>
86-
extends IModalControlled<V> {
87-
onClose?: () => void;
88-
}
91+
extends IModalControlled<V> {}

src/components/modal/types/modalTheme.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export type ModalBaseStylesType = {
1616
closeButton?: {
1717
buttonVariant?: string;
1818
};
19+
dragIconContainer?: CommonStyleType;
20+
dragIcon?: IconTypes;
1921
};
2022

2123
export type ModalStylesType<P extends string | number | symbol> = {

src/components/modalV2/fragments/modalHeader.tsx

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,76 @@ import React from 'react';
33
import { Button } from '@/components/button';
44
import { ElementOrIcon } from '@/components/elementOrIcon';
55
import { Text, TextComponentType } from '@/components/text';
6+
import { DeviceBreakpointsType } from '@/types';
67

78
import {
9+
DraggableIcon,
810
ModalCloseButtonStyled,
9-
ModalFilledContainer,
1011
ModalHeaderStyled,
1112
TitleHiddenContainer,
1213
} from '../modal.styled';
1314
import { IModalStandAlone } from '../types';
1415

15-
type PickedProps = 'styles' | 'dataTestId' | 'blocked' | 'closeIcon' | 'closeButton' | 'title';
16+
type PickedProps =
17+
| 'styles'
18+
| 'dataTestId'
19+
| 'blocked'
20+
| 'closeIcon'
21+
| 'closeButton'
22+
| 'title'
23+
| 'dragIcon'
24+
| 'dragIconRef'
25+
| 'device';
1626
type ModalHeaderProps = Pick<IModalStandAlone, PickedProps> & {
1727
titleIdFinal: string;
1828
};
1929

2030
const ModalHeaderComponent = (
2131
props: ModalHeaderProps,
2232
ref: React.ForwardedRef<HTMLDivElement> | undefined | null
23-
): JSX.Element => (
24-
<ModalHeaderStyled ref={ref} $styles={props.styles}>
25-
{/* This element is needed for aligment purpose */}
26-
<ModalFilledContainer $styles={props.styles} />
27-
{props.title?.visible === undefined || props.title.visible ? (
28-
<>
29-
<Text
30-
component={TextComponentType.H1}
31-
customTypography={props.styles.title}
32-
dataTestId={`${props.dataTestId}Title`}
33-
id={props.titleIdFinal}
34-
{...props.title}
35-
>
36-
{props.title?.content}
37-
</Text>
38-
</>
39-
) : (
40-
<TitleHiddenContainer id={props.titleIdFinal}>{props.title?.content}</TitleHiddenContainer>
41-
)}
42-
{!props.blocked && props.closeIcon?.icon && (
43-
<ModalCloseButtonStyled $styles={props.styles}>
44-
<ElementOrIcon
45-
customIconStyles={props.styles?.closeButtonIcon}
46-
dataTestId={`${props.dataTestId}CloseIcon`}
47-
{...props.closeIcon}
48-
/>
49-
</ModalCloseButtonStyled>
50-
)}
51-
{!props.blocked &&
52-
props.closeButton?.content &&
53-
(props.styles.closeButton?.buttonVariant || props.closeButton?.variant) && (
54-
<Button variant={props.styles.closeButton?.buttonVariant} {...props.closeButton}>
55-
{props.closeButton.content}
56-
</Button>
33+
): JSX.Element => {
34+
const isMobile = props.device === DeviceBreakpointsType.MOBILE;
35+
36+
return (
37+
<ModalHeaderStyled ref={ref} $styles={props.styles}>
38+
{isMobile && !props.blocked && props.dragIcon && (
39+
<DraggableIcon ref={props.dragIconRef} $styles={props.styles}>
40+
<ElementOrIcon customIconStyles={props.styles?.dragIcon} {...props.dragIcon} />
41+
</DraggableIcon>
42+
)}
43+
{!props.blocked && props.closeIcon?.icon && (
44+
<ModalCloseButtonStyled $styles={props.styles}>
45+
<ElementOrIcon
46+
customIconStyles={props.styles?.closeButtonIcon}
47+
dataTestId={`${props.dataTestId}CloseIcon`}
48+
{...props.closeIcon}
49+
/>
50+
</ModalCloseButtonStyled>
5751
)}
58-
</ModalHeaderStyled>
59-
);
52+
{!props.blocked &&
53+
props.closeButton?.content &&
54+
(props.styles.closeButton?.buttonVariant || props.closeButton?.variant) && (
55+
<Button variant={props.styles.closeButton?.buttonVariant} {...props.closeButton}>
56+
{props.closeButton.content}
57+
</Button>
58+
)}
59+
{props.title?.visible === undefined || props.title.visible ? (
60+
<>
61+
<Text
62+
component={TextComponentType.H1}
63+
customTypography={props.styles.title}
64+
dataTestId={`${props.dataTestId}Title`}
65+
id={props.titleIdFinal}
66+
{...props.title}
67+
>
68+
{props.title?.content}
69+
</Text>
70+
</>
71+
) : (
72+
<TitleHiddenContainer id={props.titleIdFinal}>{props.title?.content}</TitleHiddenContainer>
73+
)}
74+
</ModalHeaderStyled>
75+
);
76+
};
6077

6178
export const ModalHeader = React.forwardRef(ModalHeaderComponent);

src/components/modalV2/modal.styled.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ export const ModalHeaderStyled = styled.div<IModalStyled>`
8383
${props => getStyles(props.$styles.headerContainer)}
8484
`;
8585

86-
export const ModalFilledContainer = styled.div<IModalStyled>`
87-
${props => getStyles(props.$styles.closeButtonIcon)};
88-
`;
89-
9086
export const TitleHiddenContainer = styled.span`
9187
display: none;
9288
`;
@@ -105,3 +101,7 @@ export const ModalContentStyled = styled.div<IModalStyled & ModalShowScrollType>
105101
export const ModalFooterStyled = styled.div<IModalStyled>`
106102
${props => getStyles(props.$styles.footer)}
107103
`;
104+
105+
export const DraggableIcon = styled.div<IModalStyled>`
106+
${props => getStyles(props.$styles?.dragIconContainer)}
107+
`;

src/components/modalV2/modalControlled.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22

33
import { STYLES_NAME } from '@/constants';
4-
import { useMediaDevice, useScrollEffect, useStylesV2 } from '@/hooks';
4+
import { useMediaDevice, useScrollEffect, useStylesV2, useSwipeDown } from '@/hooks';
55
import { ErrorBoundary } from '@/provider/errorBoundary/errorBoundary';
66
import { FallbackComponent } from '@/provider/errorBoundary/fallbackComponent';
77

@@ -26,14 +26,26 @@ const ModalControlledComponent = React.forwardRef(
2626
shadowStyles: styles?.headerContainer?.box_shadow,
2727
});
2828

29+
const { setPopoverRef, setDragIconRef } = useSwipeDown(props.popover?.animationOptions, () =>
30+
props.onClose?.()
31+
);
32+
33+
const handlePopoverCloseInternally = () => {
34+
props.popover?.onCloseInternally?.();
35+
props.onClose?.();
36+
};
37+
2938
const modalStructure = (
3039
<ModalStandAlone
3140
{...props}
3241
ref={ref}
3342
device={device}
43+
dragIconRef={setDragIconRef}
44+
popover={{ ...props.popover, forwardedRef: setPopoverRef }}
3445
scrollableRef={scrollableRef}
3546
shadowRef={shadowRef}
3647
styles={styles as ModalBaseStylesType}
48+
onPopoverCloseInternally={handlePopoverCloseInternally}
3749
/>
3850
);
3951

0 commit comments

Comments
 (0)