Skip to content

Commit

Permalink
chore(web): refactor modal (#1000)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkumbobeaty authored Jun 3, 2024
1 parent 541baa6 commit d77d6dc
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 0 deletions.
104 changes: 104 additions & 0 deletions web/src/beta/lib/reearth-ui/components/Modal/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Meta, StoryObj } from "@storybook/react";
import { FC, useCallback, useState } from "react";

import { Button } from "../Button";
import { ModalPanel } from "../ModalPanel";

import { ModalProps, Modal as ModalComponent } from ".";

const meta: Meta<ModalProps> = {
component: ModalComponent,
};

export default meta;
type Story = StoryObj<typeof ModalComponent>;

const Modal: FC<ModalProps> = ({ size, children }: Omit<ModalProps, "visible">) => {
const [visible, setVisible] = useState(false);

const handleOpen = useCallback(() => {
setVisible(true);
}, []);

const handleClose = useCallback(() => {
setVisible(false);
}, []);

return (
<div style={{ height: "50vh" }}>
<Button title="Open Modal" appearance="primary" onClick={handleOpen} />
{visible && (
<ModalComponent size={size} visible={visible}>
{children ? (
<div style={{ padding: "24px", borderRadius: "4px", background: "#262626" }}>
{children}
<div
style={{
justifyContent: "flex-end",
display: "flex",
paddingTop: "10px",
}}>
<Button onClick={handleClose} size="normal" title="Okay" appearance="primary" />
</div>
</div>
) : (
<ModalPanel
title="Title modal"
onCancel={handleClose}
actions={
<>
<Button onClick={handleClose} size="normal" title="Cancel" />
<Button size="normal" title="Apply" appearance="primary" />
</>
}>
<div>
Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin
literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin
professor at Hampden-Sydney College in Virginia, looked up one of the more obscure
Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of
the word in classical literature, discovered the undoubtable source.
</div>
</ModalPanel>
)}
</ModalComponent>
)}
</div>
);
};

export const SmallSize: Story = {
render: args => {
return (
<Modal {...args} size="small">
<div>
<h5 style={{ margin: 0, fontSize: "16px", lineHeight: "24px" }}>Title</h5>
Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin
literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin
professor at Hampden-Sydney College in Virginia, looked up one of the more obscure
bvncfyutghjkm,
</div>
</Modal>
);
},
args: {
visible: false,
},
};

export const MediumSize: Story = {
render: args => {
return <Modal {...args} size="medium" />;
},
args: {
visible: false,
},
};

export const LargeSize: Story = {
render: args => {
return <Modal {...args} size="large" />;
},
args: {
visible: false,
},
};
57 changes: 57 additions & 0 deletions web/src/beta/lib/reearth-ui/components/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { FC, ReactNode, useMemo, useRef } from "react";

import { styled } from "@reearth/services/theme";

export type ModalProps = {
visible: boolean;
children: ReactNode;
size?: "small" | "medium" | "large";
};

const DEFAULT_SMALL_WIDTH = 416;
const DEFAULT_MEDIUM_WIDTH = 572;
const DEFAULT_LARGE_WIDTH = 778;

export const Modal: FC<ModalProps> = ({ visible, children, size }) => {
const ref = useRef<HTMLDivElement>(null);

const modalWidth = useMemo(
() =>
size === "small"
? DEFAULT_SMALL_WIDTH
: size === "large"
? DEFAULT_LARGE_WIDTH
: DEFAULT_MEDIUM_WIDTH,
[size],
);

return !visible ? null : (
<Wrapper>
<ContentWrapper modalWidth={modalWidth} ref={ref}>
{children}
</ContentWrapper>
</Wrapper>
);
};

const Wrapper = styled("div")(({ theme }) => ({
background: theme.bg.transparentBlack,
position: "fixed",
left: 0,
top: 0,
width: "100%",
height: "100%",
overflow: "auto",
opacity: 1,
zIndex: theme.zIndexes.editor.modal.bg,
}));

const ContentWrapper = styled("div")<{ modalWidth?: number }>(({ modalWidth }) => ({
margin: "0 auto",
height: "100%",
width: `${modalWidth}px`,
position: "relative",
display: "flex",
flexDirection: "column",
justifyContent: "center",
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Meta, StoryObj } from "@storybook/react";
import { FC } from "react";

import { Button } from "../Button";

import { ModalPanel, ModalPanelProps } from ".";

const meta: Meta<ModalPanelProps> = {
component: ModalPanel,
};

export default meta;

type Story = StoryObj<typeof ModalPanel>;

const MockChild: FC = () => (
<div style={{ color: "#e0e0e0", fontSize: "14px", width: "417px" }}>
<p>
Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature
from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at
Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words,
consectetur, from a Lorem Ipsum passage,
</p>
</div>
);

export const Default: Story = {
args: {
title: "Modal Panel Title",
children: <MockChild />,
actions: <Button title="Ok" appearance="primary" />,
},
};

export const MultipleActions: Story = {
args: {
title: "Modal Panel Title",
children: <MockChild />,
actions: (
<>
<Button size="normal" title="Cancel" />
<Button size="normal" title="Apply" appearance="primary" />
</>
),
},
};
65 changes: 65 additions & 0 deletions web/src/beta/lib/reearth-ui/components/ModalPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { FC, ReactNode } from "react";

import { Button } from "@reearth/beta/lib/reearth-ui";
import { fonts, styled } from "@reearth/services/theme";

export type ModalPanelProps = {
title?: string;
children: ReactNode;
actions?: ReactNode;
onCancel?: () => void;
};

export const ModalPanel: FC<ModalPanelProps> = ({ title, children, actions, onCancel }) => {
return (
<Wrapper>
<HeaderWrapper>
<Title>{title}</Title>
<Button iconButton icon="close" size="small" onClick={onCancel} appearance="simple" />
</HeaderWrapper>
<Content>{children}</Content>
{actions && <ActionWrapper>{actions}</ActionWrapper>}
</Wrapper>
);
};

const Wrapper = styled("div")(({ theme }) => ({
width: "fit-content",
display: "flex",
flexDirection: "column",
background: theme.bg.transparentBlack,
}));

const HeaderWrapper = styled("div")(({ theme }) => ({
display: "flex",
justifyContent: "space-between",
alignItems: "center",
alignSelf: "stretch",
padding: `${theme.spacing.normal}px`,
color: theme.content.main,
background: theme.bg[2],
borderTopRightRadius: theme.radius.large,
borderTopLeftRadius: theme.radius.large,
}));

const Title = styled("div")(() => ({
flex: "1 0 0",
fontSize: fonts.sizes.body,
lineHeight: `${fonts.lineHeights.body}px`,
}));

const Content = styled("div")(({ theme }) => ({
padding: theme.spacing.super,
alignSelf: "stretch",
}));

const ActionWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing.normal,
background: theme.bg[2],
borderBottomRightRadius: theme.radius.large,
borderBottomLeftRadius: theme.radius.large,
justifyContent: "flex-end",
display: "flex",
alignItems: "flex-start",
gap: theme.spacing.normal,
}));
2 changes: 2 additions & 0 deletions web/src/beta/lib/reearth-ui/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export * from "./PopupPanel";
export * from "./CodeInput";
export * from "./Collapse";
export * from "./Switcher";
export * from "./ModalPanel";
export * from "./Modal";
export * from "./ColorInput";
export * from "./DatePicker";
export * from "./TimePicker";

0 comments on commit d77d6dc

Please sign in to comment.