mirror of
https://github.com/owncast/owncast.git
synced 2025-11-03 13:01:46 +08:00
Add custom Modal component
This commit is contained in:
@ -1,4 +1,6 @@
|
|||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Modal from '../ui/Modal/Modal';
|
||||||
import { ExternalAction } from '../interfaces/external-action.interface';
|
import { ExternalAction } from '../interfaces/external-action.interface';
|
||||||
import s from './ActionButton.module.scss';
|
import s from './ActionButton.module.scss';
|
||||||
|
|
||||||
@ -7,13 +9,35 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ActionButton(props: Props) {
|
export default function ActionButton(props: Props) {
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
const { action } = props;
|
const { action } = props;
|
||||||
const { url, title, description, icon, color, openExternally } = action;
|
const { url, title, description, icon, color, openExternally } = action;
|
||||||
|
|
||||||
|
const buttonClicked = () => {
|
||||||
|
if (openExternally) {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
} else {
|
||||||
|
setShowModal(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button type="primary" className={`${s.button}`} style={{ backgroundColor: color }}>
|
<>
|
||||||
<img src={icon} className={`${s.icon}`} alt={description} />
|
<Button
|
||||||
{title}
|
type="primary"
|
||||||
</Button>
|
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)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export default function ContentComponent() {
|
|||||||
description: 'Example button description',
|
description: 'Example button description',
|
||||||
icon: 'https://owncast.online/images/logo.svg',
|
icon: 'https://owncast.online/images/logo.svg',
|
||||||
color: '#5232c8',
|
color: '#5232c8',
|
||||||
openExternally: true,
|
openExternally: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
11
web/components/ui/Modal/Modal.module.scss
Normal file
11
web/components/ui/Modal/Modal.module.scss
Normal file
@ -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
Normal file
74
web/components/ui/Modal/Modal.tsx
Normal file
@ -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,
|
||||||
|
};
|
||||||
@ -1,14 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||||
import { Modal, Button } from 'antd';
|
import Modal from '../components/ui/Modal/Modal';
|
||||||
|
|
||||||
const Usage = () => (
|
|
||||||
<Modal title="Basic Modal" visible>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
<p>Some contents...</p>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'owncast/Modal container',
|
title: 'owncast/Modal container',
|
||||||
@ -16,7 +8,21 @@ export default {
|
|||||||
parameters: {},
|
parameters: {},
|
||||||
} as ComponentMeta<typeof Modal>;
|
} as ComponentMeta<typeof Modal>;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
const Template: ComponentStory<typeof Modal> = args => {
|
||||||
const Template: ComponentStory<typeof Modal> = args => <Usage />;
|
const { children } = args;
|
||||||
|
return <Modal {...args}>{children}</Modal>;
|
||||||
|
};
|
||||||
|
|
||||||
export const Example = Template.bind({});
|
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',
|
||||||
|
};
|
||||||
|
|||||||
@ -356,47 +356,6 @@ textarea.ant-input {
|
|||||||
opacity: 0.75;
|
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
|
// SELECT
|
||||||
.ant-select-dropdown {
|
.ant-select-dropdown {
|
||||||
|
|||||||
Reference in New Issue
Block a user