diff --git a/packages/components/package.json b/packages/components/package.json index f618c47b8..570e3e4db 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -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", diff --git a/packages/components/src/components/LightBox/LightBox.module.scss b/packages/components/src/components/LightBox/LightBox.module.scss new file mode 100644 index 000000000..2f03c6fe3 --- /dev/null +++ b/packages/components/src/components/LightBox/LightBox.module.scss @@ -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; + } +} diff --git a/packages/components/src/components/LightBox/LightBox.tsx b/packages/components/src/components/LightBox/LightBox.tsx new file mode 100644 index 000000000..7403cf617 --- /dev/null +++ b/packages/components/src/components/LightBox/LightBox.tsx @@ -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 ( + + + +
{children}
+
+ + + + +
+
+
+
+ ); +}); + +export default LightBox; diff --git a/packages/components/src/components/LightBox/components/LightBoxTrigger/LightBoxTrigger.tsx b/packages/components/src/components/LightBox/components/LightBoxTrigger/LightBoxTrigger.tsx new file mode 100644 index 000000000..42a2139be --- /dev/null +++ b/packages/components/src/components/LightBox/components/LightBoxTrigger/LightBoxTrigger.tsx @@ -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 = (props) => { + const { children, ...triggerProps } = props; + return ( + + {children} + + ); +}; diff --git a/packages/components/src/components/LightBox/components/LightBoxTrigger/index.ts b/packages/components/src/components/LightBox/components/LightBoxTrigger/index.ts new file mode 100644 index 000000000..40248ebca --- /dev/null +++ b/packages/components/src/components/LightBox/components/LightBoxTrigger/index.ts @@ -0,0 +1,4 @@ +import { LightBoxTrigger } from "./LightBoxTrigger"; + +export * from "./LightBoxTrigger"; +export default LightBoxTrigger; diff --git a/packages/components/src/components/LightBox/index.ts b/packages/components/src/components/LightBox/index.ts new file mode 100644 index 000000000..f597e4c36 --- /dev/null +++ b/packages/components/src/components/LightBox/index.ts @@ -0,0 +1,5 @@ +import { LightBox } from "./LightBox"; + +export * from "./components/LightBoxTrigger"; +export { type LightBoxProps, LightBox } from "./LightBox"; +export default LightBox; diff --git a/packages/components/src/components/LightBox/stories/Default.stories.tsx b/packages/components/src/components/LightBox/stories/Default.stories.tsx new file mode 100644 index 000000000..9ecd5b403 --- /dev/null +++ b/packages/components/src/components/LightBox/stories/Default.stories.tsx @@ -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 = { + title: "Overlays/LightBox", + component: LightBox, + parameters: { + controls: { exclude: ["controller"] }, + }, + render: (props) => { + return ( + + + + + + + + + + + ); + }, +}; +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithoutFitScreen: Story = { args: { fitScreen: false } }; diff --git a/packages/components/src/components/Modal/Modal.tsx b/packages/components/src/components/Modal/Modal.tsx index 6cd6000e9..ed5c0e9be 100644 --- a/packages/components/src/components/Modal/Modal.tsx +++ b/packages/components/src/components/Modal/Modal.tsx @@ -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"; @@ -68,7 +68,7 @@ export const Modal = flowComponent("Modal", (props) => { }; return ( - +
@@ -88,7 +88,7 @@ export const Modal = flowComponent("Modal", (props) => { {children} - + ); }); diff --git a/packages/components/src/components/ModalOverlay/index.ts b/packages/components/src/components/ModalOverlay/index.ts deleted file mode 100644 index 48a3036dc..000000000 --- a/packages/components/src/components/ModalOverlay/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ModalOverlay } from "./ModalOverlay"; -export { type ModalOverlayProps, ModalOverlay } from "./ModalOverlay"; -export default ModalOverlay; diff --git a/packages/components/src/components/ModalOverlay/ModalOverlay.module.scss b/packages/components/src/components/Overlay/Overlay.module.scss similarity index 100% rename from packages/components/src/components/ModalOverlay/ModalOverlay.module.scss rename to packages/components/src/components/Overlay/Overlay.module.scss diff --git a/packages/components/src/components/ModalOverlay/ModalOverlay.tsx b/packages/components/src/components/Overlay/Overlay.tsx similarity index 79% rename from packages/components/src/components/ModalOverlay/ModalOverlay.tsx rename to packages/components/src/components/Overlay/Overlay.tsx index 7b7a80ee5..c60a4c7bb 100644 --- a/packages/components/src/components/ModalOverlay/ModalOverlay.tsx +++ b/packages/components/src/components/Overlay/Overlay.tsx @@ -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 = (props) => { +export const Overlay: FC = (props) => { const { controller: controllerFromProps, children, isDismissable = true, className, + overlayType = "Modal", } = props; - const controllerFromContext = useOverlayController("Modal", { + const controllerFromContext = useOverlayController(overlayType, { reuseControllerFromContext: true, }); @@ -49,4 +51,4 @@ export const ModalOverlay: FC = (props) => { ); }; -export default ModalOverlay; +export default Overlay; diff --git a/packages/components/src/components/Overlay/index.ts b/packages/components/src/components/Overlay/index.ts new file mode 100644 index 000000000..0a984f3d2 --- /dev/null +++ b/packages/components/src/components/Overlay/index.ts @@ -0,0 +1,4 @@ +import { Overlay } from "./Overlay"; + +export { type OverlayProps, Overlay } from "./Overlay"; +export default Overlay; diff --git a/packages/components/src/components/propTypes/index.ts b/packages/components/src/components/propTypes/index.ts index 0501f471d..35daa735c 100644 --- a/packages/components/src/components/propTypes/index.ts +++ b/packages/components/src/components/propTypes/index.ts @@ -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"; @@ -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"; @@ -90,6 +91,7 @@ export interface FlowComponentPropsTypes { Initials: InitialsProps; Label: LabelProps; LayoutCard: LayoutCardProps; + LightBox: LightBoxProps; Link: LinkProps; List: ListProps; ListSummary: ListSummaryProps; @@ -154,6 +156,7 @@ const propsContextSupportingComponentsMap: Record< Initials: true, Label: true, LayoutCard: true, + LightBox: true, Link: true, List: true, ListSummary: true, diff --git a/packages/components/vite.build.config.base.ts b/packages/components/vite.build.config.base.ts index 062a21799..f668a8a95 100644 --- a/packages/components/vite.build.config.base.ts +++ b/packages/components/vite.build.config.base.ts @@ -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": diff --git a/packages/design-tokens/src/overlays/light-box.yml b/packages/design-tokens/src/overlays/light-box.yml new file mode 100644 index 000000000..83ccbf86e --- /dev/null +++ b/packages/design-tokens/src/overlays/light-box.yml @@ -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}" diff --git a/packages/design-tokens/src/overlays/overlay.yml b/packages/design-tokens/src/overlays/overlay.yml index 09f2f06f7..aae58397e 100644 --- a/packages/design-tokens/src/overlays/overlay.yml +++ b/packages/design-tokens/src/overlays/overlay.yml @@ -1,3 +1,3 @@ overlay: background-color: - value: rgba(0, 0, 0, 0.3) + value: "{dark.color.200}" diff --git a/packages/design-tokens/src/overlays/popover.yml b/packages/design-tokens/src/overlays/popover.yml index f8be5f8f3..f729ecdb0 100644 --- a/packages/design-tokens/src/overlays/popover.yml +++ b/packages/design-tokens/src/overlays/popover.yml @@ -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: diff --git a/packages/docs/src/content/03-components/overlays/light-box/examples/actions.tsx b/packages/docs/src/content/03-components/overlays/light-box/examples/actions.tsx new file mode 100644 index 000000000..81921d283 --- /dev/null +++ b/packages/docs/src/content/03-components/overlays/light-box/examples/actions.tsx @@ -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"; + + + + + + + + + + +; diff --git a/packages/docs/src/content/03-components/overlays/light-box/examples/default.tsx b/packages/docs/src/content/03-components/overlays/light-box/examples/default.tsx new file mode 100644 index 000000000..06ce03309 --- /dev/null +++ b/packages/docs/src/content/03-components/overlays/light-box/examples/default.tsx @@ -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"; + + + + + + +; diff --git a/packages/docs/src/content/03-components/overlays/light-box/examples/fitScreenFalse.tsx b/packages/docs/src/content/03-components/overlays/light-box/examples/fitScreenFalse.tsx new file mode 100644 index 000000000..d655aefc8 --- /dev/null +++ b/packages/docs/src/content/03-components/overlays/light-box/examples/fitScreenFalse.tsx @@ -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"; + + + + + + +; diff --git a/packages/docs/src/content/03-components/overlays/light-box/examples/static.tsx b/packages/docs/src/content/03-components/overlays/light-box/examples/static.tsx new file mode 100644 index 000000000..40b3cf78a --- /dev/null +++ b/packages/docs/src/content/03-components/overlays/light-box/examples/static.tsx @@ -0,0 +1,22 @@ +import { Button } from "@mittwald/flow-react-components/Button"; +import { IconClose } from "@mittwald/flow-react-components/Icons"; +import { Image } from "@mittwald/flow-react-components/Image"; + +
+
+
+ +
+
+ +
+
+
; diff --git a/packages/docs/src/content/03-components/overlays/light-box/index.mdx b/packages/docs/src/content/03-components/overlays/light-box/index.mdx new file mode 100644 index 000000000..6bb7aee4a --- /dev/null +++ b/packages/docs/src/content/03-components/overlays/light-box/index.mdx @@ -0,0 +1,8 @@ +--- +component: LightBox +description: + Die LightBox dient dazu einzelne Elemente, wie Bilder, in einem Overlay + anzuzeigen. +--- + + diff --git a/packages/docs/src/content/03-components/overlays/light-box/overview.mdx b/packages/docs/src/content/03-components/overlays/light-box/overview.mdx new file mode 100644 index 000000000..b1741847d --- /dev/null +++ b/packages/docs/src/content/03-components/overlays/light-box/overview.mdx @@ -0,0 +1,20 @@ +# Playground + + + +--- + +# Mit ActionGroup + + + +--- + +# FitScreen + +Im Default wird der Inhalt durch die Größe des Screens begrenzt. Wird das +Property `fitScreen` auf `false` gesetzt, wird der Inhalt in seiner vollen Höhe +dargestellt, und wenn nötig mit einer Scrollbar versehen. Das kann vorallem bei +der Darstellung höherer Elemente, wie z.B. mehrseitiger PDFs hilfreich sein. + +