diff --git a/packages/modal/package.json b/packages/modal/package.json index 6bf65b9d4..62744c09a 100644 --- a/packages/modal/package.json +++ b/packages/modal/package.json @@ -38,8 +38,8 @@ "@stenajs-webui/theme": "21.19.0", "@types/react-modal": "^3.16.3", "classnames": "^2.5.1", - "react-draggable": "^4.4.5", - "react-modal": "^3.16.1" + "react-draggable": "4.4.6", + "react-modal": "3.16.1" }, "peerDependencies": { "@emotion/react": ">=11.11.4", diff --git a/packages/modal/src/declarative-modals/modal/Modal.module.css b/packages/modal/src/declarative-modals/modal/Modal.module.css index ae345f57e..a7a3d4216 100644 --- a/packages/modal/src/declarative-modals/modal/Modal.module.css +++ b/packages/modal/src/declarative-modals/modal/Modal.module.css @@ -1,5 +1,5 @@ .overlay { - --swui-modal-animation-time: var(--swui-animation-time-fast); + --swui-modal-animation-time: var(--swui-animation-time-medium); --swui-modal-overlay-bg-color: var(--swui-overlay-bg-color); --swui-modal-content-bg-color: var(--swui-white); --swui-modal-width: 960px; @@ -15,14 +15,19 @@ right: 0; bottom: 0; background-color: var(--swui-modal-overlay-bg-color); - overflow: auto; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; + opacity: 0; - animation: fadeIn var(--swui-modal-animation-time) - cubic-bezier(0.645, 0.045, 0.355, 1) both; + &.afterOpen:not(.beforeClose) { + animation: fadeIn var(--swui-modal-animation-time) forwards; + } + + &.beforeClose { + animation: fadeOut var(--swui-modal-animation-time) forwards; + } @media print { background-color: rgba(255, 255, 255, 1); @@ -32,10 +37,23 @@ max-width: 100%; outline: none; pointer-events: none; + padding: calc(var(--swui-metrics-space) * 2); + box-sizing: border-box; + max-height: 100%; + transform: translateY(-100%); @media (max-width: 768px) { + padding: 0; width: 100%; } + + &.afterOpen:not(.beforeClose) { + animation: slideIn var(--swui-modal-animation-time) forwards; + } + + &.beforeClose { + animation: slideOut var(--swui-modal-animation-time) forwards; + } } .content { @@ -49,15 +67,32 @@ width: var(--swui-modal-width); max-width: var(--swui-modal-max-width); + height: 100%; + overflow: hidden; + overflow-y: auto; + @media (max-width: 768px) { border-radius: 0; width: 100%; max-width: 100%; - height: 100vh; + min-height: 100vh; } - animation: appear var(--swui-animation-time-fast) - cubic-bezier(0.645, 0.045, 0.355, 1) both; + @media (min-width: 769px) { + /* This styling ensures border radius even with scroll bars. */ + border-radius: var(--swui-border-radius-large); + + &::-webkit-scrollbar-thumb { + background-color: var(--lhds-color-ui-400); + border: 4px solid transparent; + border-radius: 8px; + background-clip: padding-box; + } + + &::-webkit-scrollbar { + width: 16px; + } + } @media print { box-shadow: none; @@ -67,38 +102,49 @@ :focus { outline: 0; } - - &.isDraggable { - :global(.draggable-modal-handle) { - cursor: move; - - :global(.draggable-modal-cancel), - [role="tooltip"] { - cursor: initial; - } - } - } } } @keyframes fadeIn { - 0% { - background-color: var(--swui-hidden); + from { + opacity: 0; } - 100% { - background-color: var(--swui-modal-overlay-bg-color); + to { + opacity: 1; } } -@keyframes appear { - 0% { +@keyframes fadeOut { + from { + opacity: 1; + } + to { opacity: 0; } - 100% { +} + +@keyframes slideIn { + from { + transform: translateY(-100%); + opacity: 0; + } + to { + transform: translateY(0); opacity: 1; } } +@keyframes slideOut { + from { + transform: translateY(0); + opacity: 1; + } + to { + transform: translateY(-100%); + opacity: 0; + } +} + .footer { background-color: var(--swui-modal-content-bg-color); } diff --git a/packages/modal/src/declarative-modals/modal/Modal.stories.tsx b/packages/modal/src/declarative-modals/modal/Modal.stories.tsx index 359d8eee4..5a711d08c 100644 --- a/packages/modal/src/declarative-modals/modal/Modal.stories.tsx +++ b/packages/modal/src/declarative-modals/modal/Modal.stories.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { useState } from "react"; -import { Text } from "@stenajs-webui/core"; +import { Heading, Spacing, Text } from "@stenajs-webui/core"; import { PrimaryButton, stenaBell } from "@stenajs-webui/elements"; import { Modal } from "./Modal"; import { ModalBody } from "../../building-blocks/ModalBody"; @@ -79,3 +79,37 @@ export const WithInfoAlert = () => { ); }; + +export const ScrollableContent = () => { + const [open, setOpen] = useState(false); + + return ( +
+ setOpen(true)} /> + setOpen(false)}> + + Start of modal + {Array.from({ length: 20 }, (_, i) => i).map(() => ( + + Some random stuff + + ))} + setOpen(false)} + /> + } + /> + End of modal + + +
+ ); +}; diff --git a/packages/modal/src/declarative-modals/modal/Modal.tsx b/packages/modal/src/declarative-modals/modal/Modal.tsx index 5ef2e5f4c..efb9e2273 100644 --- a/packages/modal/src/declarative-modals/modal/Modal.tsx +++ b/packages/modal/src/declarative-modals/modal/Modal.tsx @@ -21,9 +21,18 @@ export const Modal: React.FC = ({ return (
{ ); }; +const ScrollableContent: React.FC = () => { + const { resolve } = useDialogPromise(); + + return ( + + Start of modal + {Array.from({ length: 20 }, (_, i) => i).map(() => ( + + Some random stuff + + ))} + + resolve()} /> + + + ); +}; + export const Overview: StoryFn = () => { const [element, { show }] = useModalDialog(ModalContent); @@ -84,6 +102,17 @@ Mobile.parameters = { }, }; +export const Scrollable: StoryFn = () => { + const [element, { show }] = useModalDialog(ScrollableContent); + + return ( + + show()} /> + {element} + + ); +}; + export const MobileWithBackground: StoryFn = () => { const [element, { show }] = useModalDialog(ModalContent, { background: cssColor("--himmel"),