mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-17 23:44:39 -05:00
Added global sticky footer component in AdminX
refs. https://github.com/TryGhost/Team/issues/3354
This commit is contained in:
parent
abc169329c
commit
76704d05c6
5 changed files with 131 additions and 34 deletions
|
@ -1,16 +1,18 @@
|
||||||
import type {Meta, StoryObj} from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
const Component = () => null;
|
import BoilerPlate from './Boilerplate';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Meta / Boilerplate story',
|
title: 'Meta / Boilerplate story',
|
||||||
component: Component,
|
component: BoilerPlate,
|
||||||
tags: ['autodocs']
|
tags: ['autodocs']
|
||||||
} satisfies Meta<typeof Component>;
|
} satisfies Meta<typeof BoilerPlate>;
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof Component>;
|
type Story = StoryObj<typeof BoilerPlate>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {}
|
args: {
|
||||||
|
children: 'This is a boilerplate component. Use as a basis to create new components.'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
15
ghost/admin-x-settings/src/admin-x-ds/Boilerplate.tsx
Normal file
15
ghost/admin-x-settings/src/admin-x-ds/Boilerplate.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface BoilerPlateProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BoilerPlate: React.FC<BoilerPlateProps> = ({children}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BoilerPlate;
|
|
@ -2,6 +2,7 @@ import Button, {IButton} from './Button';
|
||||||
import ButtonGroup from './ButtonGroup';
|
import ButtonGroup from './ButtonGroup';
|
||||||
import Heading from './Heading';
|
import Heading from './Heading';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import StickyFooter from './StickyFooter';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {useModal} from '@ebay/nice-modal-react';
|
import {useModal} from '@ebay/nice-modal-react';
|
||||||
|
|
||||||
|
@ -86,35 +87,35 @@ const Modal: React.FC<ModalProps> = ({
|
||||||
modalClasses += ' max-w-[480px] ';
|
modalClasses += ' max-w-[480px] ';
|
||||||
backdropClasses += ' p-[8vmin]';
|
backdropClasses += ' p-[8vmin]';
|
||||||
padding = 'p-8';
|
padding = 'p-8';
|
||||||
footerContainerBottom = 'calc(-1 * (8vmin + 48px)';
|
footerContainerBottom = 'calc(-1 * 8vmin)';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'md':
|
case 'md':
|
||||||
modalClasses += ' max-w-[720px] ';
|
modalClasses += ' max-w-[720px] ';
|
||||||
backdropClasses += ' p-[8vmin]';
|
backdropClasses += ' p-[8vmin]';
|
||||||
padding = 'p-8';
|
padding = 'p-8';
|
||||||
footerContainerBottom = 'calc(-1 * (8vmin + 48px)';
|
footerContainerBottom = 'calc(-1 * 8vmin)';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'lg':
|
case 'lg':
|
||||||
modalClasses += ' max-w-[1020px] ';
|
modalClasses += ' max-w-[1020px] ';
|
||||||
backdropClasses += ' p-[4vmin]';
|
backdropClasses += ' p-[4vmin]';
|
||||||
padding = 'p-12';
|
padding = 'p-12';
|
||||||
footerContainerBottom = 'calc(-1 * (4vmin + 68px)';
|
footerContainerBottom = 'calc(-1 * 4vmin)';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'xl':
|
case 'xl':
|
||||||
modalClasses += ' max-w-[1240px] ';
|
modalClasses += ' max-w-[1240px] ';
|
||||||
backdropClasses += ' p-[3vmin]';
|
backdropClasses += ' p-[3vmin]';
|
||||||
padding = 'p-12';
|
padding = 'p-12';
|
||||||
footerContainerBottom = 'calc(-1 * (3vmin + 68px)';
|
footerContainerBottom = 'calc(-1 * 3vmin)';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'full':
|
case 'full':
|
||||||
modalClasses += ' h-full ';
|
modalClasses += ' h-full ';
|
||||||
backdropClasses += ' p-[2vmin]';
|
backdropClasses += ' p-[2vmin]';
|
||||||
padding = 'p-12';
|
padding = 'p-12';
|
||||||
footerContainerBottom = 'calc(-1 * (2vmin + 68px)';
|
footerContainerBottom = 'calc(-1 * 2vmin)';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'bleed':
|
case 'bleed':
|
||||||
|
@ -124,7 +125,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||||
|
|
||||||
default:
|
default:
|
||||||
backdropClasses += ' p-[8vmin]';
|
backdropClasses += ' p-[8vmin]';
|
||||||
footerContainerBottom = 'calc(-1 * (8vmin + 48px)';
|
footerContainerBottom = 'calc(-1 * 8vmin)';
|
||||||
padding = 'p-8';
|
padding = 'p-8';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -133,16 +134,11 @@ const Modal: React.FC<ModalProps> = ({
|
||||||
padding = 'p-0';
|
padding = 'p-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(
|
let footerClasses = clsx(
|
||||||
`${padding} ${stickyFooter ? 'pt-8' : 'pt-0'} z-[101] flex items-center justify-between`,
|
`${padding} ${stickyFooter ? 'py-6' : 'pt-0'}`,
|
||||||
stickyFooter && `sticky bottom-[-48px] rounded-b bg-white`
|
'flex w-full items-center justify-between'
|
||||||
|
// `${padding} ${stickyFooter ? 'pt-8' : 'pt-0'} z-[101]`,
|
||||||
|
// stickyFooter && `sticky bottom-[-48px] rounded-b bg-white`
|
||||||
);
|
);
|
||||||
|
|
||||||
let contentClasses = `${padding} h-full`;
|
let contentClasses = `${padding} h-full`;
|
||||||
|
@ -161,6 +157,29 @@ const Modal: React.FC<ModalProps> = ({
|
||||||
width: size + 'px'
|
width: size + 'px'
|
||||||
} : {};
|
} : {};
|
||||||
|
|
||||||
|
const footerContent = (
|
||||||
|
<div className={footerClasses}>
|
||||||
|
<div>
|
||||||
|
{leftButtonLabel &&
|
||||||
|
<Button label={leftButtonLabel} link={true} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div className='flex gap-3'>
|
||||||
|
<ButtonGroup buttons={buttons}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const footer = (stickyFooter ?
|
||||||
|
<StickyFooter bgTWColor='white' shiftY={footerContainerBottom}>
|
||||||
|
{footerContent}
|
||||||
|
</StickyFooter>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
{footerContent}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={backdropClasses} id='modal-backdrop' onClick={handleBackdropClick}>
|
<div className={backdropClasses} id='modal-backdrop' onClick={handleBackdropClick}>
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
|
@ -175,20 +194,7 @@ const Modal: React.FC<ModalProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{customFooter ? customFooter :
|
{customFooter ? customFooter :
|
||||||
<div className={footerContainerClasses} style={{
|
footer
|
||||||
bottom: `${stickyFooter && footerContainerBottom}`
|
|
||||||
}}>
|
|
||||||
<div className={footerClasses}>
|
|
||||||
<div>
|
|
||||||
{leftButtonLabel &&
|
|
||||||
<Button label={leftButtonLabel} link={true} />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className='flex gap-3'>
|
|
||||||
<ButtonGroup buttons={buttons}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
|
import StickyFooter from './StickyFooter';
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: 'Global / Sticky Footer',
|
||||||
|
component: StickyFooter,
|
||||||
|
tags: ['autodocs'],
|
||||||
|
decorators: [(_story: any) => (
|
||||||
|
<div style={{
|
||||||
|
maxWidth: '600px',
|
||||||
|
margin: '0 auto 80px',
|
||||||
|
background: '#efefef'
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
height: '1500px'
|
||||||
|
}}></div>
|
||||||
|
{_story()}
|
||||||
|
</div>
|
||||||
|
)]
|
||||||
|
} satisfies Meta<typeof StickyFooter>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof StickyFooter>;
|
||||||
|
|
||||||
|
const footerContents = (
|
||||||
|
<div className='p-6'>
|
||||||
|
Hello sticky footer
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
children: footerContents
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
interface StickyFooterProps {
|
||||||
|
shiftY?: string;
|
||||||
|
bgTWColor: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
|
||||||
|
containerClasses?: string;
|
||||||
|
contentClasses?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StickyFooter: React.FC<StickyFooterProps> = ({shiftY, bgTWColor = 'white', children, containerClasses, contentClasses}) => {
|
||||||
|
let footerContainerBottom = shiftY || '0';
|
||||||
|
|
||||||
|
let footerContainerClasses = clsx(
|
||||||
|
'w-100 sticky z-[100] mb-[-24px]',
|
||||||
|
`after:sticky after:bottom-[22px] after:mx-2 after:block after:h-[22px] after:rounded-full after:shadow-[0_0_0_1px_rgba(0,0,0,.025),0_-8px_16px_-3px_rgba(0,0,0,.08)] after:content-['']`,
|
||||||
|
containerClasses
|
||||||
|
);
|
||||||
|
|
||||||
|
let footerClasses = clsx(
|
||||||
|
`bg-${bgTWColor} sticky z-[101] mb-[-24px] flex min-h-[48px] items-center justify-between`,
|
||||||
|
contentClasses
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={footerContainerClasses} style={{
|
||||||
|
bottom: footerContainerBottom
|
||||||
|
}}>
|
||||||
|
<div className={footerClasses}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StickyFooter;
|
Loading…
Add table
Reference in a new issue