From 9405d3825724b87b7ba1b0f2027a9cbbee9fd74a Mon Sep 17 00:00:00 2001 From: SchnozzleCat Date: Tue, 7 Apr 2026 10:21:06 +0200 Subject: [PATCH] feat(modal): add positioning --- .../BoemlyModal/BoemlyModal.stories.tsx | 23 +++++++ src/components/BoemlyModal/BoemlyModal.tsx | 60 ++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/components/BoemlyModal/BoemlyModal.stories.tsx b/src/components/BoemlyModal/BoemlyModal.stories.tsx index 10f525a..17d74da 100644 --- a/src/components/BoemlyModal/BoemlyModal.stories.tsx +++ b/src/components/BoemlyModal/BoemlyModal.stories.tsx @@ -29,6 +29,20 @@ export default { options: ['xs', 'sm', 'md', 'lg', 'xl', 'full', 'cover'], control: { type: 'radio' }, }, + position: { + options: [ + 'topLeft', + 'top', + 'topRight', + 'left', + 'center', + 'right', + 'bottomLeft', + 'bottom', + 'bottomRight', + ], + control: { type: 'select' }, + }, }, } as Meta; @@ -70,6 +84,15 @@ Size.args = { size: 'xl', }; +export const Position = Template.bind({}); +Position.args = { + title: 'Centered Modal', + content:
Use the `position` prop to align the modal within the viewport.
, + footer: , + size: 'md', + position: 'center', +}; + export const WithCustomTitle = Template.bind({}); WithCustomTitle.args = { title: ( diff --git a/src/components/BoemlyModal/BoemlyModal.tsx b/src/components/BoemlyModal/BoemlyModal.tsx index 048483b..b196d92 100644 --- a/src/components/BoemlyModal/BoemlyModal.tsx +++ b/src/components/BoemlyModal/BoemlyModal.tsx @@ -7,6 +7,16 @@ export interface BoemlyModalProps { trigger: ReactNode; footer?: ReactNode | ((props: { onClose: () => void }) => ReactNode); size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full' | 'cover'; + position?: + | 'topLeft' + | 'top' + | 'topRight' + | 'left' + | 'center' + | 'right' + | 'bottomLeft' + | 'bottom' + | 'bottomRight'; open: boolean; onOpenChange: (open: boolean) => void; } @@ -33,11 +43,57 @@ export const BoemlyModal: React.FC = ({ trigger, footer, size = 'xl', + position = 'center', open, onOpenChange, }: BoemlyModalProps) => { const onClose = () => onOpenChange(false); + const positionStyles: Record< + NonNullable, + { + positioner: { justifyContent: string; alignItems: string }; + content: { marginInlineStart: string; marginInlineEnd: string }; + } + > = { + topLeft: { + positioner: { justifyContent: 'flex-start', alignItems: 'flex-start' }, + content: { marginInlineStart: '0', marginInlineEnd: 'auto' }, + }, + top: { + positioner: { justifyContent: 'center', alignItems: 'flex-start' }, + content: { marginInlineStart: 'auto', marginInlineEnd: 'auto' }, + }, + topRight: { + positioner: { justifyContent: 'flex-end', alignItems: 'flex-start' }, + content: { marginInlineStart: 'auto', marginInlineEnd: '0' }, + }, + left: { + positioner: { justifyContent: 'flex-start', alignItems: 'center' }, + content: { marginInlineStart: '0', marginInlineEnd: 'auto' }, + }, + center: { + positioner: { justifyContent: 'center', alignItems: 'center' }, + content: { marginInlineStart: 'auto', marginInlineEnd: 'auto' }, + }, + right: { + positioner: { justifyContent: 'flex-end', alignItems: 'center' }, + content: { marginInlineStart: 'auto', marginInlineEnd: '0' }, + }, + bottomLeft: { + positioner: { justifyContent: 'flex-start', alignItems: 'flex-end' }, + content: { marginInlineStart: '0', marginInlineEnd: 'auto' }, + }, + bottom: { + positioner: { justifyContent: 'center', alignItems: 'flex-end' }, + content: { marginInlineStart: 'auto', marginInlineEnd: 'auto' }, + }, + bottomRight: { + positioner: { justifyContent: 'flex-end', alignItems: 'flex-end' }, + content: { marginInlineStart: 'auto', marginInlineEnd: '0' }, + }, + }; + const renderFooter = () => { if (!footer) return null; @@ -55,8 +111,8 @@ export const BoemlyModal: React.FC = ({ {trigger} - - + + {title}