ui/src/hooks/useToast/index.tsx (76 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { useLayoutEffect, useState } from 'react';
import { Toast } from 'react-bootstrap';
import ReactDOM from 'react-dom/client';
const toastPortal = document.createElement('div');
toastPortal.style.position = 'fixed';
toastPortal.style.top = '78px';
toastPortal.style.left = '50%';
toastPortal.style.transform = 'translate(-50%, 0)';
toastPortal.style.maxWidth = '100%';
toastPortal.style.zIndex = '1001';
const setPortalPosition = () => {
const header = document.querySelector('#header');
if (header) {
toastPortal.style.top = `${header.getBoundingClientRect().top + 78}px`;
}
};
const startHandlePortalPosition = () => {
setPortalPosition();
window.addEventListener('scroll', setPortalPosition);
};
const stopHandlePortalPosition = () => {
setPortalPosition();
window.removeEventListener('scroll', setPortalPosition);
};
const root = ReactDOM.createRoot(toastPortal);
interface Params {
/** main content */
msg: string;
/** theme color */
variant?: 'warning' | 'success' | 'danger';
}
const useToast = () => {
const [show, setShow] = useState(false);
const [data, setData] = useState<Params>({
msg: '',
variant: 'warning',
});
const onClose = () => {
const parent = document.querySelector('.page-wrap');
if (parent?.contains(toastPortal)) {
parent.removeChild(toastPortal);
}
stopHandlePortalPosition();
setShow(false);
};
const onShow = (t: Params) => {
setData(t);
startHandlePortalPosition();
setShow(true);
};
useLayoutEffect(() => {
const parent = document.querySelector('.page-wrap');
parent?.appendChild(toastPortal);
root.render(
<div className="d-flex justify-content-center">
<Toast
className="align-items-center border-0"
delay={5000}
bg={data.variant || 'warning'}
show={show}
autohide
onClose={onClose}>
<div className="d-flex">
<Toast.Body
dangerouslySetInnerHTML={{ __html: data.msg }}
className={`${data.variant !== 'warning' ? 'text-white' : ''}`}
/>
<button
className={`btn-close me-2 m-auto ${
data.variant !== 'warning' ? 'btn-close-white' : ''
}`}
onClick={onClose}
data-bs-dismiss="toast"
aria-label="Close"
/>
</div>
</Toast>
</div>,
);
}, [show, data]);
return {
onShow,
};
};
export default useToast;