Browse Source

Add custom Modal component

pull/2032/head
Gabe Kangas 3 years ago
parent
commit
92a1da4df6
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
  1. 32
      web/components/action-buttons/ActionButton.tsx
  2. 2
      web/components/ui/Content/Content.tsx
  3. 11
      web/components/ui/Modal/Modal.module.scss
  4. 74
      web/components/ui/Modal/Modal.tsx
  5. 28
      web/stories/Modal.stories.tsx
  6. 41
      web/styles/ant-overrides.scss

32
web/components/action-buttons/ActionButton.tsx

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
import { Button } from 'antd';
import { useState } from 'react';
import Modal from '../ui/Modal/Modal';
import { ExternalAction } from '../interfaces/external-action.interface';
import s from './ActionButton.module.scss';
@ -7,13 +9,35 @@ interface Props { @@ -7,13 +9,35 @@ interface Props {
}
export default function ActionButton(props: Props) {
const [showModal, setShowModal] = useState(false);
const { action } = props;
const { url, title, description, icon, color, openExternally } = action;
const buttonClicked = () => {
if (openExternally) {
window.open(url, '_blank');
} else {
setShowModal(true);
}
};
return (
<Button type="primary" className={`${s.button}`} style={{ backgroundColor: color }}>
<img src={icon} className={`${s.icon}`} alt={description} />
{title}
</Button>
<>
<Button
type="primary"
className={`${s.button}`}
onClick={buttonClicked}
style={{ backgroundColor: color }}
>
<img src={icon} className={`${s.icon}`} alt={description} />
{title}
</Button>
<Modal
title={description || title}
url={url}
visible={showModal}
handleCancel={() => setShowModal(false)}
/>
</>
);
}

2
web/components/ui/Content/Content.tsx

@ -49,7 +49,7 @@ export default function ContentComponent() { @@ -49,7 +49,7 @@ export default function ContentComponent() {
description: 'Example button description',
icon: 'https://owncast.online/images/logo.svg',
color: '#5232c8',
openExternally: true,
openExternally: false,
},
];

11
web/components/ui/Modal/Modal.module.scss

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
.spinner {
position: absolute;
top: 50%;
left: 50%;
}
.content {
display: block;
height: 100%;
padding: 2vw;
}

74
web/components/ui/Modal/Modal.tsx

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
import { Spin, Skeleton, Modal as AntModal } from 'antd';
import React, { ReactNode, useState } from 'react';
import s from './Modal.module.scss';
interface Props {
title: string;
url?: string;
visible: boolean;
handleOk?: () => void;
handleCancel?: () => void;
afterClose?: () => void;
children?: ReactNode;
}
export default function Modal(props: Props) {
const { title, url, visible, handleOk, handleCancel, afterClose, children } = props;
const [loading, setLoading] = useState(!!url);
const modalStyle = {
padding: '0px',
height: '80vh',
};
console.log(url);
const iframe = url && (
<iframe
title={title}
src={url}
width="100%"
height="100%"
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
allowpaymentrequest="true"
frameBorder="0"
allowFullScreen
onLoad={() => setLoading(false)}
/>
);
const iframeDisplayStyle = loading ? 'none' : 'inline';
return (
<AntModal
title={title}
visible={visible}
onOk={handleOk}
onCancel={handleCancel}
afterClose={afterClose}
bodyStyle={modalStyle}
width="70%"
zIndex={9999}
footer={null}
centered
destroyOnClose
>
<>
{loading && (
<Skeleton active={loading} style={{ padding: '10px' }} paragraph={{ rows: 10 }} />
)}
{iframe && <div style={{ display: iframeDisplayStyle }}>{iframe}</div>}
{children && <div className={s.content}>{children}</div>}
{loading && <Spin className={s.spinner} spinning={loading} size="large" />}
</>
</AntModal>
);
}
Modal.defaultProps = {
url: undefined,
children: undefined,
handleOk: undefined,
handleCancel: undefined,
afterClose: undefined,
};

28
web/stories/Modal.stories.tsx

@ -1,14 +1,6 @@ @@ -1,14 +1,6 @@
import React, { useState } from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Modal, Button } from 'antd';
const Usage = () => (
<Modal title="Basic Modal" visible>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
);
import Modal from '../components/ui/Modal/Modal';
export default {
title: 'owncast/Modal container',
@ -16,7 +8,21 @@ export default { @@ -16,7 +8,21 @@ export default {
parameters: {},
} as ComponentMeta<typeof Modal>;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Template: ComponentStory<typeof Modal> = args => <Usage />;
const Template: ComponentStory<typeof Modal> = args => {
const { children } = args;
return <Modal {...args}>{children}</Modal>;
};
export const Example = Template.bind({});
Example.args = {
title: 'Modal example with content nodes',
visible: true,
children: <div>Test 123</div>,
};
export const UrlExample = Template.bind({});
UrlExample.args = {
title: 'Modal example with URL',
visible: true,
url: 'https://owncast.online',
};

41
web/styles/ant-overrides.scss

@ -356,47 +356,6 @@ textarea.ant-input { @@ -356,47 +356,6 @@ textarea.ant-input {
opacity: 0.75;
}
// MODAL
.ant-modal,
.ant-modal-body {
font-size: 1em;
}
.ant-modal-content {
border-radius: var(--container-border-radius);
border: 1px solid var(--owncast-purple);
background-color: var(--black);
}
.ant-modal-header {
border-radius: var(--container-border-radius) var(--container-border-radius) 0 0;
}
.ant-modal-close-x {
color: var(--white);
}
.ant-modal-title {
font-weight: 500;
font-size: 1.25em;
color: var(--nav-selected-text);
}
.ant-modal-body {
background-color: var(--gray);
color: var(--default-text-color);
}
.ant-modal-header,
.ant-modal-footer {
background: var(--black);
}
.ant-modal-content,
.ant-modal-header,
.ant-modal-footer {
border-color: var(--white-50);
}
.ant-modal-confirm-body {
.ant-modal-confirm-title,
.ant-modal-confirm-content {
color: var(--default-text-color);
}
}
// SELECT
.ant-select-dropdown {

Loading…
Cancel
Save