Web App: Fix button styles (#2478)
This commit is contained in:
parent
172f03f474
commit
573583ce67
138
src/components/main/WebAppModal.module.scss
Normal file
138
src/components/main/WebAppModal.module.scss
Normal file
@ -0,0 +1,138 @@
|
||||
.root {
|
||||
:global {
|
||||
.modal-header {
|
||||
border-bottom: 1px solid var(--color-dividers);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
height: 75%;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
border-bottom-right-radius: var(--border-radius-default);
|
||||
border-bottom-left-radius: var(--border-radius-default);
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.modal-dialog {
|
||||
background-color: var(--color-background);
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
max-height: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
transform: rotate(-45deg);
|
||||
|
||||
&,
|
||||
&::before,
|
||||
&::after {
|
||||
width: 1.125rem;
|
||||
height: 0.125rem;
|
||||
border-radius: 0.125rem;
|
||||
background-color: var(--color-text-secondary);
|
||||
transition: transform var(--slide-transition);
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
content: "";
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.state-back {
|
||||
transform: rotate(180deg);
|
||||
|
||||
&::before {
|
||||
transform: rotate(45deg) scaleX(0.75) translate(0, -0.375rem);
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: rotate(-45deg) scaleX(0.75) translate(0, 0.375rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
z-index: 1;
|
||||
|
||||
&.with-button {
|
||||
height: calc(100% - 3.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
.main-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
border-radius: 0;
|
||||
transform: translateY(100%);
|
||||
transition: 0.25s ease-in-out transform;
|
||||
|
||||
&.visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.main-button-spinner {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.web-app-popup {
|
||||
:global(.modal-dialog) {
|
||||
max-width: min(30rem, 100%);
|
||||
}
|
||||
|
||||
&.without-title :global(.modal-content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
:global(.modal-content) {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
.WebAppModal {
|
||||
.modal-header {
|
||||
border-bottom: 1px solid var(--color-dividers);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
height: 75%;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
border-bottom-right-radius: var(--border-radius-default);
|
||||
border-bottom-left-radius: var(--border-radius-default);
|
||||
}
|
||||
|
||||
.web-app-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
|
||||
&.with-button {
|
||||
height: calc(100% - 56px);
|
||||
}
|
||||
}
|
||||
|
||||
.web-app-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
border-radius: 0;
|
||||
transform: translateY(100%);
|
||||
transition: 0.25s ease-in-out transform;
|
||||
|
||||
&.visible {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.Spinner {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.modal-dialog {
|
||||
background-color: var(--color-background);
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
max-height: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.animated-close-icon {
|
||||
position: absolute;
|
||||
transform: rotate(-45deg);
|
||||
|
||||
&,
|
||||
&::before,
|
||||
&::after {
|
||||
width: 1.125rem;
|
||||
height: 0.125rem;
|
||||
border-radius: 0.125rem;
|
||||
background-color: var(--color-text-secondary);
|
||||
transition: transform var(--slide-transition);
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
content: "";
|
||||
}
|
||||
|
||||
&::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
&.state-back {
|
||||
transform: rotate(180deg);
|
||||
|
||||
&::before {
|
||||
transform: rotate(45deg) scaleX(0.75) translate(0, -0.375rem);
|
||||
}
|
||||
|
||||
&::after {
|
||||
transform: rotate(-45deg) scaleX(0.75) translate(0, 0.375rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.web-app-popup {
|
||||
.modal-dialog {
|
||||
max-width: min(30rem, 100%);
|
||||
}
|
||||
|
||||
&.without-title .modal-content {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ import MenuItem from '../ui/MenuItem';
|
||||
import Spinner from '../ui/Spinner';
|
||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||
|
||||
import './WebAppModal.scss';
|
||||
import styles from './WebAppModal.module.scss';
|
||||
|
||||
type WebAppButton = {
|
||||
isVisible: boolean;
|
||||
@ -101,6 +101,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
const [headerColor, setHeaderColor] = useState(extractCurrentThemeParams().bg_color);
|
||||
const [confirmClose, setConfirmClose] = useState(false);
|
||||
const [isCloseModalOpen, openCloseModal, closeCloseModal] = useFlag(false);
|
||||
const [isLoaded, markLoaded, markUnloaded] = useFlag(false);
|
||||
const [popupParams, setPopupParams] = useState<PopupOptions | undefined>();
|
||||
const { isMobile } = useAppLayout();
|
||||
const prevPopupParams = usePrevious(popupParams);
|
||||
@ -204,7 +205,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const {
|
||||
reloadFrame, sendEvent, sendViewport, sendTheme,
|
||||
} = useWebAppFrame(frameRef, isOpen, isSimple, handleEvent);
|
||||
} = useWebAppFrame(frameRef, isOpen, isSimple, handleEvent, markLoaded);
|
||||
|
||||
const shouldShowMainButton = mainButton?.isVisible && mainButton.text.trim().length > 0;
|
||||
|
||||
@ -315,8 +316,9 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
setConfirmClose(false);
|
||||
closeCloseModal();
|
||||
setPopupParams(undefined);
|
||||
markUnloaded();
|
||||
}
|
||||
}, [closeCloseModal, isOpen]);
|
||||
}, [closeCloseModal, isOpen, markUnloaded]);
|
||||
|
||||
const MoreMenuButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
|
||||
return ({ onTrigger, isOpen: isMenuOpen }) => (
|
||||
@ -335,8 +337,8 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
}, [isMobile]);
|
||||
|
||||
const backButtonClassName = buildClassName(
|
||||
'animated-close-icon',
|
||||
isBackButtonVisible && 'state-back',
|
||||
styles.closeIcon,
|
||||
isBackButtonVisible && styles.stateBack,
|
||||
);
|
||||
|
||||
const header = useMemo(() => {
|
||||
@ -361,6 +363,11 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
<MenuItem icon="bots" onClick={openBotChat}>{lang('BotWebViewOpenBot')}</MenuItem>
|
||||
)}
|
||||
<MenuItem icon="reload" onClick={handleRefreshClick}>{lang('WebApp.ReloadPage')}</MenuItem>
|
||||
{attachBot?.hasSettings && (
|
||||
<MenuItem icon="settings" onClick={handleSettingsButtonClick}>
|
||||
{lang('Settings')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{bot?.isAttachBot && (
|
||||
<MenuItem
|
||||
icon={attachBot ? 'stop' : 'install'}
|
||||
@ -370,11 +377,6 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
{lang(attachBot ? 'WebApp.RemoveBot' : 'WebApp.AddToAttachmentAdd')}
|
||||
</MenuItem>
|
||||
)}
|
||||
{attachBot?.hasSettings && (
|
||||
<MenuItem icon="settings" onClick={handleSettingsButtonClick}>
|
||||
{lang('Settings')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
@ -425,17 +427,18 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="WebAppModal"
|
||||
className={styles.root}
|
||||
isOpen={isOpen}
|
||||
onClose={handleClose}
|
||||
header={header}
|
||||
hasCloseButton
|
||||
style={`background-color: ${backgroundColor}`}
|
||||
>
|
||||
<Spinner className={buildClassName(styles.loadingSpinner, isLoaded && styles.hide)} />
|
||||
{isOpen && (
|
||||
<>
|
||||
<iframe
|
||||
className={buildClassName('web-app-frame', shouldDecreaseWebFrameSize && 'with-button')}
|
||||
className={buildClassName(styles.frame, shouldDecreaseWebFrameSize && styles.withButton)}
|
||||
src={url}
|
||||
title={`${bot?.firstName} Web App`}
|
||||
sandbox={SANDBOX_ATTRIBUTES}
|
||||
@ -445,16 +448,16 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
<Button
|
||||
className={buildClassName(
|
||||
'web-app-button',
|
||||
shouldShowMainButton && 'visible',
|
||||
shouldHideButton && 'hidden',
|
||||
styles.mainButton,
|
||||
shouldShowMainButton && styles.visible,
|
||||
shouldHideButton && styles.hidden,
|
||||
)}
|
||||
style={`background-color: ${mainButtonCurrentColor}; color: ${mainButtonCurrentTextColor}`}
|
||||
disabled={!mainButtonCurrentIsActive}
|
||||
onClick={handleMainButtonClick}
|
||||
>
|
||||
{mainButtonCurrentText}
|
||||
{mainButton?.isProgressVisible && <Spinner color="white" />}
|
||||
{mainButton?.isProgressVisible && <Spinner className={styles.mainButtonSpinner} color="white" />}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
@ -475,16 +478,18 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
title={renderingPopupParams.title || NBSP}
|
||||
onClose={handlePopupModalClose}
|
||||
hasCloseButton
|
||||
className={buildClassName('web-app-popup', !renderingPopupParams.title?.trim().length && 'without-title')}
|
||||
className={
|
||||
buildClassName(styles.webAppPopup, !renderingPopupParams.title?.trim().length && styles.withoutTitle)
|
||||
}
|
||||
>
|
||||
{renderingPopupParams.message}
|
||||
<div className="dialog-buttons mt-2">
|
||||
{renderingPopupParams.buttons.map((button) => (
|
||||
<Button
|
||||
key={button.id || button.text || button.type}
|
||||
key={button.id || button.type}
|
||||
className="confirm-dialog-button"
|
||||
color={button.type === 'destructive' ? 'danger' : 'primary'}
|
||||
isText
|
||||
fluid
|
||||
size="smaller"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handlePopupClose(button.id)}
|
||||
|
||||
@ -169,11 +169,26 @@ const useWebAppFrame = (
|
||||
isOpen: boolean,
|
||||
isSimpleView: boolean,
|
||||
onEvent: (event: WebAppInboundEvent) => void,
|
||||
onLoad?: () => void,
|
||||
) => {
|
||||
const ignoreEventsRef = useRef<boolean>(false);
|
||||
const lastFrameSizeRef = useRef<{ width: number; height: number; isResizing?: boolean }>();
|
||||
const windowSize = useWindowSize();
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current || !isOpen) return undefined;
|
||||
|
||||
const handleLoad = () => {
|
||||
onLoad?.();
|
||||
};
|
||||
|
||||
const frame = ref.current;
|
||||
frame.addEventListener('load', handleLoad);
|
||||
return () => {
|
||||
frame.removeEventListener('load', handleLoad);
|
||||
};
|
||||
}, [onLoad, ref, isOpen]);
|
||||
|
||||
const reloadFrame = useCallback((url: string) => {
|
||||
if (!ref.current) return;
|
||||
const frame = ref.current;
|
||||
@ -228,6 +243,10 @@ const useWebAppFrame = (
|
||||
try {
|
||||
const data = JSON.parse(event.data) as WebAppInboundEvent;
|
||||
// Handle some app requests here to simplify hook usage
|
||||
if (data.eventType === 'web_app_ready') {
|
||||
onLoad?.();
|
||||
}
|
||||
|
||||
if (data.eventType === 'web_app_request_viewport') {
|
||||
sendViewport(windowSize.isResizing);
|
||||
}
|
||||
@ -263,7 +282,7 @@ const useWebAppFrame = (
|
||||
} catch (err) {
|
||||
// Ignore other messages
|
||||
}
|
||||
}, [isSimpleView, onEvent, sendCustomStyle, sendEvent, sendTheme, sendViewport, windowSize.isResizing]);
|
||||
}, [isSimpleView, onEvent, sendCustomStyle, sendEvent, sendTheme, sendViewport, onLoad, windowSize.isResizing]);
|
||||
|
||||
useEffect(() => {
|
||||
const { width, height, isResizing } = windowSize;
|
||||
|
||||
@ -185,7 +185,7 @@ const Button: FC<OwnProps> = ({
|
||||
title={ariaLabel}
|
||||
tabIndex={tabIndex}
|
||||
dir={isRtl ? 'rtl' : undefined}
|
||||
style={buildStyle(backgroundImage && `background-image: url(${backgroundImage})`)}
|
||||
style={buildStyle(style, backgroundImage && `background-image: url(${backgroundImage})`)}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div>
|
||||
|
||||
@ -174,7 +174,7 @@
|
||||
flex-wrap: wrap;
|
||||
|
||||
.confirm-dialog-button + .confirm-dialog-button {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ const Spinner: FC<{
|
||||
}) => {
|
||||
return (
|
||||
<div className={buildClassName(
|
||||
'Spinner', className, color, backgroundColor && 'with-background', `bg-${backgroundColor}`,
|
||||
'Spinner', className, color, backgroundColor && 'with-background', backgroundColor && `bg-${backgroundColor}`,
|
||||
)}
|
||||
>
|
||||
<div />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user