0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-08 02:52:39 -05:00

Added preview modal to AdminX Design System

refs. https://github.com/TryGhost/Team/issues/3328
This commit is contained in:
Peter Zimon 2023-05-30 18:53:29 +02:00
parent ee274deae6
commit 0e5d66b680
5 changed files with 183 additions and 14 deletions

View file

@ -18,9 +18,10 @@ interface IHeading {
* Uses <label> tag and standardised styling for form labels
*/
useLabelTag?: boolean;
className?: string;
}
const Heading: React.FC<IHeading> = ({level, children, styles, grey, separator, useLabelTag, ...props}) => {
const Heading: React.FC<IHeading> = ({level, children, styles, grey, separator, useLabelTag, className, ...props}) => {
if (!level) {
level = 1;
}
@ -28,7 +29,7 @@ const Heading: React.FC<IHeading> = ({level, children, styles, grey, separator,
const newElement = `${useLabelTag ? 'label' : `h${level}`}`;
styles += (level === 6 || useLabelTag) ? (` block text-2xs font-semibold uppercase tracking-wide ${(grey && 'text-grey-700')}`) : ' ';
const Element = React.createElement(newElement, {className: styles, key: 'heading-elem', ...props}, children);
const Element = React.createElement(newElement, {className: styles + ' ' + className, key: 'heading-elem', ...props}, children);
if (separator) {
let gap = (!level || level === 1) ? 2 : 1;

View file

@ -14,12 +14,25 @@ export interface ModalProps {
cancelLabel?: string;
leftButtonLabel?: string;
customFooter?: React.ReactNode;
noPadding?: boolean;
onOk?: () => void;
onCancel?: () => void;
children?: React.ReactNode;
}
const Modal: React.FC<ModalProps> = ({size = 'md', title, okLabel, cancelLabel, customFooter, leftButtonLabel, onOk, okColor, onCancel, children}) => {
const Modal: React.FC<ModalProps> = ({
size = 'md',
title,
okLabel = 'OK',
cancelLabel = 'Cancel',
customFooter,
leftButtonLabel,
noPadding = false,
onOk,
okColor = 'black',
onCancel,
children
}) => {
const modal = useModal();
let buttons: IButton[] = [];
@ -27,7 +40,7 @@ const Modal: React.FC<ModalProps> = ({size = 'md', title, okLabel, cancelLabel,
if (!customFooter) {
buttons.push({
key: 'cancel-modal',
label: cancelLabel ? cancelLabel : 'Cancel',
label: cancelLabel,
onClick: (onCancel ? onCancel : () => {
modal.remove();
})
@ -35,8 +48,8 @@ const Modal: React.FC<ModalProps> = ({size = 'md', title, okLabel, cancelLabel,
buttons.push({
key: 'ok-modal',
label: okLabel ? okLabel : 'OK',
color: okColor ? okColor : 'black',
label: okLabel,
color: okColor,
styles: 'min-w-[80px]',
onClick: onOk
});
@ -47,32 +60,32 @@ const Modal: React.FC<ModalProps> = ({size = 'md', title, okLabel, cancelLabel,
switch (size) {
case 'sm':
modalStyles += ' max-w-[480px] p-8';
modalStyles += ` max-w-[480px] ${!noPadding && 'p-8'}`;
backdropStyles += ' p-[8vmin]';
break;
case 'md':
modalStyles += ' max-w-[720px] p-8';
modalStyles += ` max-w-[720px] ${!noPadding && 'p-8'}`;
backdropStyles += ' p-[8vmin]';
break;
case 'lg':
modalStyles += ' max-w-[1020px] p-12';
modalStyles += ` max-w-[1020px] ${!noPadding && 'p-12'}`;
backdropStyles += ' p-[4vmin]';
break;
case 'xl':
modalStyles += ' max-w-[1240px] p-14';
modalStyles += ` max-w-[1240px] ${!noPadding && 'p-14'}`;
backdropStyles += ' p-[3vmin]';
break;
case 'full':
modalStyles += ' h-full p-12';
modalStyles += ` h-full ${!noPadding && 'p-12'}`;
backdropStyles += ' p-[2vmin]';
break;
case 'bleed':
modalStyles += ' h-full p-12';
modalStyles += ` h-full ${!noPadding && 'p-12'}`;
break;
}
@ -86,9 +99,9 @@ const Modal: React.FC<ModalProps> = ({size = 'md', title, okLabel, cancelLabel,
<div className={backdropStyles} id='modal-backdrop' onClick={handleBackdropClick}>
<div className='pointer-events-none fixed inset-0 z-0 bg-[rgba(0,0,0,0.1)]'></div>
<section className={modalStyles}>
<div>
<div className='h-full'>
{title && <Heading level={4}>{title}</Heading>}
<div>{children}</div>
{children}
</div>
{customFooter ? customFooter :
<div className='w-100 flex items-center justify-between gap-6'>

View file

@ -0,0 +1,56 @@
import type {Meta, StoryObj} from '@storybook/react';
import Heading from './Heading';
import NiceModal from '@ebay/nice-modal-react';
import PreviewModal from './PreviewModal';
import PreviewModalContainer from './PreviewModalContainer';
const meta = {
title: 'Global / Modal / Preview Modal',
component: PreviewModal,
tags: ['autodocs'],
decorators: [(_story: any, context: any) => (
<NiceModal.Provider>
<PreviewModalContainer {...context.args} />
</NiceModal.Provider>
)]
} satisfies Meta<typeof PreviewModal>;
export default meta;
type Story = StoryObj<typeof PreviewModal>;
export const Default: Story = {
args: {
title: 'Preview modal',
preview: (
<div className='flex h-full items-center justify-center text-sm text-grey-500'>
Preview area
</div>
),
sidebar: (
<div className='flex h-full items-center justify-center text-sm text-grey-500'>
Sidebar area
</div>
)
}
};
export const CustomButtons: Story = {
args: {
...Default.args,
cancelLabel: 'Meh',
okLabel: 'Alrite',
okColor: 'green'
}
};
export const CustomHeader: Story = {
args: {
...Default.args,
customHeader: (
<div className='border-b border-grey-100 bg-black p-10 text-center text-white'>
<Heading level={3}>A custom header here</Heading>
</div>
)
}
};

View file

@ -0,0 +1,85 @@
import ButtonGroup from './ButtonGroup';
import Heading from './Heading';
import Modal from './Modal';
import NiceModal, {useModal} from '@ebay/nice-modal-react';
import React from 'react';
import {IButton} from './Button';
export interface PreviewModalProps {
title?: string;
sidebar?: React.ReactNode;
preview?: React.ReactNode;
cancelLabel?: string;
okLabel?: string;
okColor?: string;
onCancel?: () => void;
onOk?: () => void;
customButtons?: React.ReactNode;
customHeader?: React.ReactNode;
sidebarPadding?: boolean;
}
const PreviewModal: React.FC<PreviewModalProps> = ({
title,
sidebar,
preview,
cancelLabel = 'Cancel',
okLabel = 'OK',
okColor = 'black',
onCancel,
onOk,
customButtons,
customHeader,
sidebarPadding = true
}) => {
const modal = useModal();
let buttons: IButton[] = [];
if (!customButtons) {
buttons.push({
key: 'cancel-modal',
label: cancelLabel,
onClick: (onCancel ? onCancel : () => {
modal.remove();
})
});
buttons.push({
key: 'ok-modal',
label: okLabel,
color: okColor,
styles: 'min-w-[80px]',
onClick: onOk
});
}
return (
<Modal
customFooter={(<></>)}
noPadding={true}
size='full'
title=''
>
<div className='flex h-full grow'>
<div className='grow'>
{preview}
</div>
<div className='flex h-full basis-[400px] flex-col gap-3 border-l border-grey-100'>
{customHeader ? customHeader : (
<div className='flex justify-between gap-3 px-7 pt-7'>
<>
<Heading className='mt-1' level={4}>{title}</Heading>
{customButtons ? customButtons : <ButtonGroup buttons={buttons} /> }
</>
</div>
)}
<div className={`grow ${sidebarPadding && 'p-7'}`}>
{sidebar}
</div>
</div>
</div>
</Modal>
);
};
export default NiceModal.create(PreviewModal);

View file

@ -0,0 +1,14 @@
import Button from './Button';
import NiceModal from '@ebay/nice-modal-react';
import PreviewModal, {PreviewModalProps} from './PreviewModal';
import React from 'react';
const PreviewModalContainer: React.FC<PreviewModalProps> = ({...props}) => {
return (
<Button color='black' label='Open preview modal' onClick={() => {
NiceModal.show(PreviewModal, {...props});
}} />
);
};
export default PreviewModalContainer;