mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 21:40:39 +03:00
Added sticky footer option to modals in AdminX
refs. https://github.com/TryGhost/Team/issues/3351
This commit is contained in:
parent
f5415a25ad
commit
19dfd28946
@ -31,7 +31,7 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
||||
const [taskState, setTaskState] = useState<'running' | ''>('');
|
||||
return (
|
||||
<Modal
|
||||
backDrop={false}
|
||||
backDropClick={false}
|
||||
cancelLabel={cancelLabel}
|
||||
customFooter={customFooter}
|
||||
okColor={okColor}
|
||||
@ -45,7 +45,7 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
||||
setTaskState('');
|
||||
}}
|
||||
>
|
||||
<div className='py-4'>
|
||||
<div className='py-4 leading-9'>
|
||||
{prompt}
|
||||
</div>
|
||||
</Modal>
|
||||
|
@ -13,7 +13,7 @@ const meta = {
|
||||
<ModalContainer {...context.args} />
|
||||
</NiceModal.Provider>
|
||||
)]
|
||||
|
||||
|
||||
} satisfies Meta<typeof Modal>;
|
||||
|
||||
export default meta;
|
||||
@ -108,4 +108,31 @@ export const CustomButtons: Story = {
|
||||
title: 'Custom buttons',
|
||||
children: modalContent
|
||||
}
|
||||
};
|
||||
|
||||
const longContent = (
|
||||
<>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure. Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure. Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p className='mb-6'>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
<p>Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure. Esse ex officia ipsum et magna reprehenderit ullamco dolore cillum cupidatat ullamco culpa. In et irure irure est id cillum officia pariatur et proident. Nulla nulla dolore qui excepteur magna eu adipisicing mollit. Eiusmod eu irure cupidatat consequat consectetur irure.</p>
|
||||
</>
|
||||
);
|
||||
|
||||
export const StickyFooter: Story = {
|
||||
args: {
|
||||
size: 'md',
|
||||
stickyFooter: true,
|
||||
onOk: () => {
|
||||
alert('Clicked OK!');
|
||||
},
|
||||
title: 'Sticky footer',
|
||||
children: longContent
|
||||
}
|
||||
};
|
@ -26,6 +26,7 @@ export interface ModalProps {
|
||||
children?: React.ReactNode;
|
||||
backDrop?: boolean;
|
||||
backDropClick?: boolean;
|
||||
stickyFooter?: boolean;
|
||||
}
|
||||
|
||||
const Modal: React.FC<ModalProps> = ({
|
||||
@ -41,69 +42,113 @@ const Modal: React.FC<ModalProps> = ({
|
||||
onCancel,
|
||||
children,
|
||||
backDrop = true,
|
||||
backDropClick = true
|
||||
backDropClick = true,
|
||||
stickyFooter = false
|
||||
}) => {
|
||||
const modal = useModal();
|
||||
|
||||
let buttons: IButton[] = [];
|
||||
|
||||
if (!customFooter) {
|
||||
buttons.push({
|
||||
key: 'cancel-modal',
|
||||
label: cancelLabel,
|
||||
onClick: (onCancel ? onCancel : () => {
|
||||
modal.remove();
|
||||
})
|
||||
});
|
||||
if (cancelLabel) {
|
||||
buttons.push({
|
||||
key: 'cancel-modal',
|
||||
label: cancelLabel,
|
||||
onClick: (onCancel ? onCancel : () => {
|
||||
modal.remove();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
buttons.push({
|
||||
key: 'ok-modal',
|
||||
label: okLabel,
|
||||
color: okColor,
|
||||
className: 'min-w-[80px]',
|
||||
onClick: onOk
|
||||
});
|
||||
if (okLabel) {
|
||||
buttons.push({
|
||||
key: 'ok-modal',
|
||||
label: okLabel,
|
||||
color: okColor,
|
||||
className: 'min-w-[80px]',
|
||||
onClick: onOk
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let modalClasses = 'relative rounded overflow-hidden z-50 mx-auto flex flex-col justify-between bg-white shadow-xl w-full';
|
||||
let backdropClasses = 'fixed inset-0 h-[100vh] w-[100vw] overflow-y-scroll z-40 ';
|
||||
//bg-[linear-gradient(0deg,rgba(255,255,255,1)_85%,rgba(255,255,255,0)_100%)]
|
||||
let modalClasses = clsx('relative z-50 mx-auto flex w-full flex-col justify-between rounded bg-white shadow-xl');
|
||||
let backdropClasses = clsx('fixed inset-0 z-40 h-[100vh] w-[100vw] overflow-y-scroll ');
|
||||
|
||||
let padding;
|
||||
|
||||
let footerContainerBottom = '';
|
||||
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
modalClasses += ` max-w-[480px] ${!noPadding && 'p-8'}`;
|
||||
modalClasses += ' max-w-[480px] ';
|
||||
backdropClasses += ' p-[8vmin]';
|
||||
padding = 8;
|
||||
footerContainerBottom = 'calc(-1 * (8vmin + 48px)';
|
||||
break;
|
||||
|
||||
case 'md':
|
||||
modalClasses += ` max-w-[720px] ${!noPadding && 'p-8'}`;
|
||||
modalClasses += ' max-w-[720px] ';
|
||||
backdropClasses += ' p-[8vmin]';
|
||||
padding = 8;
|
||||
footerContainerBottom = 'calc(-1 * (8vmin + 48px)';
|
||||
break;
|
||||
|
||||
case 'lg':
|
||||
modalClasses += ` max-w-[1020px] ${!noPadding && 'p-12'}`;
|
||||
modalClasses += ' max-w-[1020px] ';
|
||||
backdropClasses += ' p-[4vmin]';
|
||||
padding = 12;
|
||||
footerContainerBottom = 'calc(-1 * (4vmin + 68px)';
|
||||
break;
|
||||
|
||||
case 'xl':
|
||||
modalClasses += ` max-w-[1240px] ${!noPadding && 'p-14'}`;
|
||||
modalClasses += ' max-w-[1240px] ';
|
||||
backdropClasses += ' p-[3vmin]';
|
||||
padding = 14;
|
||||
footerContainerBottom = 'calc(-1 * (3vmin + 68px)';
|
||||
break;
|
||||
|
||||
case 'full':
|
||||
modalClasses += ` h-full ${!noPadding && 'p-12'}`;
|
||||
modalClasses += ' h-full ';
|
||||
backdropClasses += ' p-[2vmin]';
|
||||
padding = 12;
|
||||
footerContainerBottom = 'calc(-1 * (2vmin + 68px)';
|
||||
break;
|
||||
|
||||
case 'bleed':
|
||||
modalClasses += ` h-full ${!noPadding && 'p-12'}`;
|
||||
modalClasses += ' h-full ';
|
||||
padding = 12;
|
||||
break;
|
||||
|
||||
default:
|
||||
modalClasses += ` ${!noPadding && 'p-8'}`;
|
||||
backdropClasses += ' p-[8vmin]';
|
||||
footerContainerBottom = 'calc(-1 * (8vmin + 48px)';
|
||||
padding = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
if (noPadding) {
|
||||
padding = 0;
|
||||
}
|
||||
|
||||
let footerContainerClasses = clsx(
|
||||
'w-100',
|
||||
stickyFooter && 'sticky z-[100] mb-[-24px]',
|
||||
`${stickyFooter ? 'before:sticky' : 'before:hidden'} before:bottom-0 before:z-[100] before:block before:h-[24px] before:bg-white before:content-['']`,
|
||||
`${stickyFooter ? 'after:sticky' : 'after:hidden'} after:bottom-[72px] after:block after:h-[24px] after:shadow-[0_0_0_1px_rgba(0,0,0,.04),0_-8px_16px_-3px_rgba(0,0,0,.15)] after:content-['']`
|
||||
);
|
||||
|
||||
let footerClasses = clsx(
|
||||
`px-${padding} pb-${padding} ${stickyFooter && `pt-8`} z-[101] flex items-center justify-between`,
|
||||
stickyFooter && `sticky bottom-[-48px] rounded-b bg-white`
|
||||
);
|
||||
|
||||
let contentClasses = `p-${padding}`;
|
||||
|
||||
if (!customFooter) {
|
||||
contentClasses += ' pb-0 ';
|
||||
}
|
||||
|
||||
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (e.target === e.currentTarget && backDropClick) {
|
||||
modal.remove();
|
||||
@ -121,19 +166,25 @@ const Modal: React.FC<ModalProps> = ({
|
||||
backDrop && 'bg-[rgba(98,109,121,0.15)] backdrop-blur-[3px]'
|
||||
)}></div>
|
||||
<section className={modalClasses} style={modalStyles}>
|
||||
<div className='h-full'>
|
||||
{title && <Heading level={4}>{title}</Heading>}
|
||||
{children}
|
||||
<div className={contentClasses}>
|
||||
<div className='h-full'>
|
||||
{title && <Heading level={4}>{title}</Heading>}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
{customFooter ? customFooter :
|
||||
<div className='w-100 flex items-center justify-between gap-6'>
|
||||
<div>
|
||||
{leftButtonLabel &&
|
||||
<div className={footerContainerClasses} style={{
|
||||
bottom: `${stickyFooter && footerContainerBottom}`
|
||||
}}>
|
||||
<div className={footerClasses}>
|
||||
<div>
|
||||
{leftButtonLabel &&
|
||||
<Button label={leftButtonLabel} link={true} />
|
||||
}
|
||||
</div>
|
||||
<div className='flex gap-3'>
|
||||
<ButtonGroup buttons={buttons}/>
|
||||
}
|
||||
</div>
|
||||
<div className='flex gap-3'>
|
||||
<ButtonGroup buttons={buttons}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -593,6 +593,7 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user, updateUser}) => {
|
||||
okColor='green'
|
||||
okLabel={okLabel}
|
||||
size='lg'
|
||||
stickyFooter={true}
|
||||
onOk={async () => {
|
||||
setSaveState('saving');
|
||||
if (!validator.isEmail(userData.email)) {
|
||||
|
Loading…
Reference in New Issue
Block a user