Skip to content

Commit

Permalink
feat(LightBox): add LightBox component (#972)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lisa18289 authored Nov 18, 2024
1 parent c692dba commit b648941
Show file tree
Hide file tree
Showing 23 changed files with 314 additions and 14 deletions.
5 changes: 5 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@
"types": "./dist/js/types/components/LayoutCard/index.d.ts",
"import": "./dist/js/LayoutCard.js"
},
"./LightBox/styles.css": "./dist/css/LightBox.css",
"./LightBox": {
"types": "./dist/js/types/components/LightBox/index.d.ts",
"import": "./dist/js/LightBox.js"
},
"./Link/styles.css": "./dist/css/Link.css",
"./Link": {
"types": "./dist/js/types/components/Link/index.d.ts",
Expand Down
32 changes: 32 additions & 0 deletions packages/components/src/components/LightBox/LightBox.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.lightBox {
background-color: var(--light-box--overlay-background-color);

[role="dialog"] {
outline: none;
display: flex;
justify-content: center;
column-gap: var(--light-box--spacing);
max-width: var(--light-box--max-width);
max-height: var(--light-box--max-height);
}

.content {
overflow: auto;
}

&.fitScreen {
.content {
> * {
max-height: var(--light-box--max-height);
}
}
}

.actions,
.actionGroup [role="group"] {
display: flex;
row-gap: var(--light-box--spacing);
flex-direction: column;
flex-shrink: 0;
}
}
76 changes: 76 additions & 0 deletions packages/components/src/components/LightBox/LightBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
flowComponent,
type FlowComponentProps,
} from "@/lib/componentFactory/flowComponent";
import type { PropsWithChildren } from "react";
import React from "react";
import type { PropsWithClassName } from "@/lib/types/props";
import { Overlay } from "@/components/Overlay";
import clsx from "clsx";
import type { OverlayController } from "@/lib/controller";
import type { PropsContext } from "@/lib/propsContext";
import { PropsContextProvider } from "@/lib/propsContext";
import { Button } from "@/components/Button";
import { IconClose } from "@/components/Icon/components/icons";
import { Action } from "@/components/Action";
import styles from "./LightBox.module.scss";
import { TunnelExit, TunnelProvider } from "@mittwald/react-tunnel";

export interface LightBoxProps
extends PropsWithChildren,
FlowComponentProps,
PropsWithClassName {
controller?: OverlayController;
fitScreen?: boolean;
}

export const LightBox = flowComponent("LightBox", (props) => {
const {
controller,
children,
refProp: ignoredRef,
className,
fitScreen = true,
...rest
} = props;

const rootClassName = clsx(
styles.lightBox,
fitScreen && styles.fitScreen,
className,
);

const propsContext: PropsContext = {
ActionGroup: {
className: styles.actionGroup,
Button: { variant: "soft", color: "light" },
tunnelId: "actionGroup",
ignoreBreakpoint: true,
},
};

return (
<Overlay
overlayType="LightBox"
className={rootClassName}
controller={controller}
{...rest}
>
<PropsContextProvider props={propsContext}>
<TunnelProvider>
<div className={styles.content}>{children}</div>
<div className={styles.actions}>
<Action closeOverlay="LightBox">
<Button color="light" variant="soft">
<IconClose />
</Button>
</Action>
<TunnelExit id="actionGroup" />
</div>
</TunnelProvider>
</PropsContextProvider>
</Overlay>
);
});

export default LightBox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Aria from "react-aria-components";
import type { FC } from "react";
import React from "react";
import type { OverlayTriggerProps } from "@/components/OverlayTrigger";
import { OverlayTrigger } from "@/components/OverlayTrigger";

export const LightBoxTrigger: FC<OverlayTriggerProps> = (props) => {
const { children, ...triggerProps } = props;
return (
<OverlayTrigger
overlayType="LightBox"
{...triggerProps}
component={Aria.DialogTrigger}
>
{children}
</OverlayTrigger>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { LightBoxTrigger } from "./LightBoxTrigger";

export * from "./LightBoxTrigger";
export default LightBoxTrigger;
5 changes: 5 additions & 0 deletions packages/components/src/components/LightBox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { LightBox } from "./LightBox";

export * from "./components/LightBoxTrigger";
export { type LightBoxProps, LightBox } from "./LightBox";
export default LightBox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";
import Button from "@/components/Button";
import { ActionGroup } from "@/components/ActionGroup";
import { LightBox } from "@/components/LightBox";
import LightBoxTrigger from "@/components/LightBox/components/LightBoxTrigger";
import { Image } from "@/components/Image";
import { dummyText } from "@/lib/dev/dummyText";
import { IconDelete, IconDownload } from "@/components/Icon/components/icons";

const meta: Meta<typeof LightBox> = {
title: "Overlays/LightBox",
component: LightBox,
parameters: {
controls: { exclude: ["controller"] },
},
render: (props) => {
return (
<LightBoxTrigger>
<Button>Open LightBox</Button>
<LightBox {...props}>
<Image src={dummyText.imageSrc} />
<ActionGroup>
<Button>
<IconDownload />
</Button>
<Button>
<IconDelete />
</Button>
</ActionGroup>
</LightBox>
</LightBoxTrigger>
);
},
};
export default meta;

type Story = StoryObj<typeof LightBox>;

export const Default: Story = {};

export const WithoutFitScreen: Story = { args: { fitScreen: false } };
6 changes: 3 additions & 3 deletions packages/components/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { TunnelExit, TunnelProvider } from "@mittwald/react-tunnel";
import type { OverlayController } from "@/lib/controller/overlay";
import type { FlowComponentProps } from "@/lib/componentFactory/flowComponent";
import { flowComponent } from "@/lib/componentFactory/flowComponent";
import { ModalOverlay } from "@/components/ModalOverlay";
import { Overlay } from "@/components/Overlay";
import { Header } from "@/components/Header";
import { Action } from "@/components/Action";
import { Button } from "@/components/Button";
Expand Down Expand Up @@ -68,7 +68,7 @@ export const Modal = flowComponent("Modal", (props) => {
};

return (
<ModalOverlay className={rootClassName} controller={controller} {...rest}>
<Overlay className={rootClassName} controller={controller} {...rest}>
<PropsContextProvider props={propsContext}>
<TunnelProvider>
<Header className={styles.header}>
Expand All @@ -88,7 +88,7 @@ export const Modal = flowComponent("Modal", (props) => {
{children}
</TunnelProvider>
</PropsContextProvider>
</ModalOverlay>
</Overlay>
);
});

Expand Down
3 changes: 0 additions & 3 deletions packages/components/src/components/ModalOverlay/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import * as Aria from "react-aria-components";
import type { FC, PropsWithChildren } from "react";
import React from "react";
import styles from "./ModalOverlay.module.scss";
import styles from "./Overlay.module.scss";
import clsx from "clsx";
import type { OverlayController } from "@/lib/controller";
import { useOverlayController } from "@/lib/controller";
import OverlayContextProvider from "@/lib/controller/overlay/OverlayContextProvider";

export interface ModalOverlayProps extends PropsWithChildren {
export interface OverlayProps extends PropsWithChildren {
controller?: OverlayController;
isDismissable?: boolean;
className?: string;
overlayType?: "Modal" | "LightBox";
}

export const ModalOverlay: FC<ModalOverlayProps> = (props) => {
export const Overlay: FC<OverlayProps> = (props) => {
const {
controller: controllerFromProps,
children,
isDismissable = true,
className,
overlayType = "Modal",
} = props;

const controllerFromContext = useOverlayController("Modal", {
const controllerFromContext = useOverlayController(overlayType, {
reuseControllerFromContext: true,
});

Expand Down Expand Up @@ -49,4 +51,4 @@ export const ModalOverlay: FC<ModalOverlayProps> = (props) => {
);
};

export default ModalOverlay;
export default Overlay;
4 changes: 4 additions & 0 deletions packages/components/src/components/Overlay/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Overlay } from "./Overlay";

export { type OverlayProps, Overlay } from "./Overlay";
export default Overlay;
5 changes: 4 additions & 1 deletion packages/components/src/components/propTypes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { LabelProps } from "@/components/Label";
import type { ContentProps } from "@/components/Content";
import type { LayoutCardProps } from "@/components/LayoutCard";
import type { LinkProps } from "@/components/Link";
import type { LightBoxProps } from "@/components/LightBox";
import type { FieldErrorProps } from "@/components/FieldError";
import type { FieldDescriptionProps } from "@/components/FieldDescription";
import type { AlertProps } from "@/components/Alert";
Expand Down Expand Up @@ -51,7 +52,7 @@ import type { TimeFieldProps } from "@/components/TimeField";
import type { AlertIconProps } from "@/components/AlertIcon";
import type { ListSummaryProps } from "@/components/List/components/ListSummary/ListSummary";
import type { SegmentedControlProps } from "@/components/SegmentedControl";
import type { SegmentProps } from "src/components/SegmentedControl/components/Segment";
import type { SegmentProps } from "@/components/SegmentedControl/components/Segment";
import type { FileCardProps } from "@/components/FileCard";
import type { FileFieldProps } from "@/components/FileField";

Expand Down Expand Up @@ -90,6 +91,7 @@ export interface FlowComponentPropsTypes {
Initials: InitialsProps;
Label: LabelProps;
LayoutCard: LayoutCardProps;
LightBox: LightBoxProps;
Link: LinkProps;
List: ListProps<never>;
ListSummary: ListSummaryProps;
Expand Down Expand Up @@ -154,6 +156,7 @@ const propsContextSupportingComponentsMap: Record<
Initials: true,
Label: true,
LayoutCard: true,
LightBox: true,
Link: true,
List: true,
ListSummary: true,
Expand Down
1 change: 1 addition & 0 deletions packages/components/vite.build.config.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const buildConfig = (opts: Options) => {
Label: "./src/components/Label/index.ts",
LabeledValue: "./src/components/LabeledValue/index.ts",
LayoutCard: "./src/components/LayoutCard/index.ts",
LightBox: "./src/components/LightBox/index.ts",
Link: "./src/components/Link/index.ts",
List: "./src/components/List/index.ts",
"List/ListLoaderAsyncResource":
Expand Down
9 changes: 9 additions & 0 deletions packages/design-tokens/src/overlays/light-box.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
light-box:
overlay-background-color:
value: "{dark.color.600}"
max-width:
value: "calc(100dvw - {size-px.l})"
max-height:
value: "calc(100dvh - {size-px.l})"
spacing:
value: "{size-px.s}"
2 changes: 1 addition & 1 deletion packages/design-tokens/src/overlays/overlay.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
overlay:
background-color:
value: rgba(0, 0, 0, 0.3)
value: "{dark.color.200}"
2 changes: 1 addition & 1 deletion packages/design-tokens/src/overlays/popover.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ popover:
box-shadow:
value: "{shadow.overlay}"
corner-radius:
value: "{size-px.xs}"
value: "{corner-radius.default}"
padding:
value: "{size-px.m}"
background-color:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import ActionGroup from "@mittwald/flow-react-components/ActionGroup";
import Button from "@mittwald/flow-react-components/Button";
import {
LightBox,
LightBoxTrigger,
} from "@mittwald/flow-react-components/LightBox";
import { Image } from "@mittwald/flow-react-components/Image";
import {
IconDelete,
IconDownload,
} from "@mittwald/flow-react-components/Icons";

<LightBoxTrigger>
<Button>Open LightBox</Button>
<LightBox>
<Image src="https://mittwald.github.io/flow/assets/mittwald_logo_rgb.jpg" />
<ActionGroup>
<Button>
<IconDownload />
</Button>
<Button>
<IconDelete />
</Button>
</ActionGroup>
</LightBox>
</LightBoxTrigger>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Button from "@mittwald/flow-react-components/Button";
import {
LightBox,
LightBoxTrigger,
} from "@mittwald/flow-react-components/LightBox";
import { Image } from "@mittwald/flow-react-components/Image";

<LightBoxTrigger>
<Button>Open LightBox</Button>
<LightBox>
<Image src="https://mittwald.github.io/flow/assets/mittwald_logo_rgb.jpg" />
</LightBox>
</LightBoxTrigger>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Button from "@mittwald/flow-react-components/Button";
import {
LightBox,
LightBoxTrigger,
} from "@mittwald/flow-react-components/LightBox";
import { Image } from "@mittwald/flow-react-components/Image";

<LightBoxTrigger>
<Button>Open LightBox</Button>
<LightBox fitScreen={false}>
<Image src="https://mittwald.github.io/flow/assets/mittwald_logo_rgb.jpg" />
</LightBox>
</LightBoxTrigger>;
Loading

0 comments on commit b648941

Please sign in to comment.