mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-21 21:32:13 +01:00
shrink tos modal and make it scrollable when vertical space is not enough (#373)
This commit is contained in:
parent
04924870c1
commit
2715c2ce30
@ -18,6 +18,10 @@ body {
|
|||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.plain-link {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
|
@ -3,10 +3,65 @@
|
|||||||
|
|
||||||
import { Button } from "@/app/element/button";
|
import { Button } from "@/app/element/button";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import { forwardRef } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
import "./modal.less";
|
import "./modal.less";
|
||||||
|
|
||||||
|
interface ModalProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
description?: string;
|
||||||
|
okLabel?: string;
|
||||||
|
cancelLabel?: string;
|
||||||
|
className?: string;
|
||||||
|
onClickBackdrop?: () => void;
|
||||||
|
onOk?: () => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Modal = forwardRef<HTMLDivElement, ModalProps>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
description,
|
||||||
|
cancelLabel,
|
||||||
|
okLabel,
|
||||||
|
onCancel,
|
||||||
|
onOk,
|
||||||
|
onClose,
|
||||||
|
onClickBackdrop,
|
||||||
|
}: ModalProps,
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const renderBackdrop = (onClick) => <div className="modal-backdrop" onClick={onClick}></div>;
|
||||||
|
|
||||||
|
const renderFooter = () => {
|
||||||
|
return onOk || onCancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderModal = () => (
|
||||||
|
<div className="modal-wrapper">
|
||||||
|
{renderBackdrop(onClickBackdrop)}
|
||||||
|
<div ref={ref} className={clsx(`modal`, className)}>
|
||||||
|
<Button className="secondary ghost modal-close-btn" onClick={onClose} title="Close (ESC)">
|
||||||
|
<i className="fa-sharp fa-solid fa-xmark"></i>
|
||||||
|
</Button>
|
||||||
|
<div className="content-wrapper">
|
||||||
|
<ModalContent>{children}</ModalContent>
|
||||||
|
</div>
|
||||||
|
{renderFooter() && (
|
||||||
|
<ModalFooter onCancel={onCancel} onOk={onOk} cancelLabel={cancelLabel} okLabel={okLabel} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return ReactDOM.createPortal(renderModal(), document.getElementById("main"));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
interface ModalContentProps {
|
interface ModalContentProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
@ -39,75 +94,36 @@ const ModalFooter = ({ onCancel, onOk, cancelLabel = "Cancel", okLabel = "Ok" }:
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ModalProps {
|
|
||||||
children?: React.ReactNode;
|
|
||||||
description?: string;
|
|
||||||
okLabel?: string;
|
|
||||||
cancelLabel?: string;
|
|
||||||
className?: string;
|
|
||||||
onClickBackdrop?: () => void;
|
|
||||||
onOk?: () => void;
|
|
||||||
onCancel?: () => void;
|
|
||||||
onClose?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Modal = ({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
description,
|
|
||||||
cancelLabel,
|
|
||||||
okLabel,
|
|
||||||
onCancel,
|
|
||||||
onOk,
|
|
||||||
onClose,
|
|
||||||
onClickBackdrop,
|
|
||||||
}: ModalProps) => {
|
|
||||||
const renderBackdrop = (onClick) => <div className="modal-backdrop" onClick={onClick}></div>;
|
|
||||||
|
|
||||||
const renderFooter = () => {
|
|
||||||
return onOk || onCancel;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderModal = () => (
|
|
||||||
<div className="modal-wrapper">
|
|
||||||
{renderBackdrop(onClickBackdrop)}
|
|
||||||
<div className={clsx(`modal`, className)}>
|
|
||||||
<Button className="secondary ghost modal-close-btn" onClick={onClose} title="Close (ESC)">
|
|
||||||
<i className="fa-sharp fa-solid fa-xmark"></i>
|
|
||||||
</Button>
|
|
||||||
<div className="content-wrapper">
|
|
||||||
<ModalContent>{children}</ModalContent>
|
|
||||||
</div>
|
|
||||||
{renderFooter() && (
|
|
||||||
<ModalFooter onCancel={onCancel} onOk={onOk} cancelLabel={cancelLabel} okLabel={okLabel} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return ReactDOM.createPortal(renderModal(), document.getElementById("main"));
|
|
||||||
};
|
|
||||||
|
|
||||||
interface FlexiModalProps {
|
interface FlexiModalProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClickBackdrop?: () => void;
|
onClickBackdrop?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlexiModal = ({ children, className, onClickBackdrop }: FlexiModalProps) => {
|
interface FlexiModalComponent
|
||||||
const renderBackdrop = (onClick) => <div className="modal-backdrop" onClick={onClick}></div>;
|
extends React.ForwardRefExoticComponent<FlexiModalProps & React.RefAttributes<HTMLDivElement>> {
|
||||||
|
Content: typeof ModalContent;
|
||||||
|
Footer: typeof ModalFooter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FlexiModal = forwardRef<HTMLDivElement, FlexiModalProps>(
|
||||||
|
({ children, className, onClickBackdrop }: FlexiModalProps, ref) => {
|
||||||
|
const renderBackdrop = (onClick: () => void) => <div className="modal-backdrop" onClick={onClick}></div>;
|
||||||
|
|
||||||
const renderModal = () => (
|
const renderModal = () => (
|
||||||
<div className="modal-wrapper">
|
<div className="modal-wrapper">
|
||||||
{renderBackdrop(onClickBackdrop)}
|
{renderBackdrop(onClickBackdrop)}
|
||||||
<div className={`modal ${className}`}>{children}</div>
|
<div className={`modal ${className}`} ref={ref}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return ReactDOM.createPortal(renderModal(), document.getElementById("main"));
|
return ReactDOM.createPortal(renderModal(), document.getElementById("main")!);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
FlexiModal.Content = ModalContent;
|
(FlexiModal as FlexiModalComponent).Content = ModalContent;
|
||||||
FlexiModal.Footer = ModalFooter;
|
(FlexiModal as FlexiModalComponent).Footer = ModalFooter;
|
||||||
|
|
||||||
export { FlexiModal, Modal };
|
export { FlexiModal, Modal };
|
||||||
|
@ -2,21 +2,22 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
.tos-modal {
|
.tos-modal {
|
||||||
width: 640px;
|
width: 560px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
.modal-inner {
|
.modal-inner {
|
||||||
padding: 40px 76px;
|
|
||||||
gap: 32px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 30px;
|
||||||
|
|
||||||
header.tos-header {
|
header.tos-header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--sizing-sm, 12px);
|
gap: 8px;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 36px;
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@ -48,12 +49,17 @@
|
|||||||
gap: 32px;
|
gap: 32px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.check-toggle-wrapper .toggle-label {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
.content-section {
|
.content-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 32px;
|
gap: 18px;
|
||||||
|
|
||||||
.icon-wrapper {
|
.icon-wrapper {
|
||||||
.icon {
|
.icon {
|
||||||
@ -82,9 +88,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content-section-text {
|
.content-section-text {
|
||||||
color: rgba(255, 255, 255, 0.7);
|
color: var(--secondary-text-color);
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
||||||
|
b {
|
||||||
|
color: var(--main-text-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-section-field {
|
.content-section-field {
|
||||||
@ -102,10 +112,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-label {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
@ -123,7 +129,6 @@
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-bottom: 28px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button.disabled-button {
|
button.disabled-button {
|
||||||
|
@ -6,19 +6,43 @@ import { Button } from "@/app/element/button";
|
|||||||
import { Toggle } from "@/app/element/toggle";
|
import { Toggle } from "@/app/element/toggle";
|
||||||
import { WshServer } from "@/app/store/wshserver";
|
import { WshServer } from "@/app/store/wshserver";
|
||||||
import * as services from "@/store/services";
|
import * as services from "@/store/services";
|
||||||
import { useEffect, useState } from "react";
|
import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { FlexiModal } from "./modal";
|
import { FlexiModal } from "./modal";
|
||||||
|
|
||||||
import "./tos.less";
|
import "./tos.less";
|
||||||
|
|
||||||
const TosModal = () => {
|
const TosModal = () => {
|
||||||
const [telemetryEnabled, setTelemetryEnabled] = useState<boolean>(true);
|
const [telemetryEnabled, setTelemetryEnabled] = useState<boolean>(true);
|
||||||
|
const modalRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
const updateModalHeight = () => {
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
if (modalRef.current) {
|
||||||
|
const modalHeight = modalRef.current.offsetHeight;
|
||||||
|
const maxHeight = windowHeight * 0.9;
|
||||||
|
if (maxHeight < modalHeight) {
|
||||||
|
modalRef.current.style.height = `${maxHeight}px`;
|
||||||
|
} else {
|
||||||
|
modalRef.current.style.height = "auto";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateModalHeight(); // Run on initial render
|
||||||
|
|
||||||
|
window.addEventListener("resize", updateModalHeight); // Run on window resize
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", updateModalHeight);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const acceptTos = () => {
|
const acceptTos = () => {
|
||||||
services.ClientService.AgreeTos();
|
services.ClientService.AgreeTos();
|
||||||
};
|
};
|
||||||
|
|
||||||
function setTelemetry(value: boolean) {
|
const setTelemetry = (value: boolean) => {
|
||||||
WshServer.SetConfigCommand({ "telemetry:enabled": value })
|
WshServer.SetConfigCommand({ "telemetry:enabled": value })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setTelemetryEnabled(value);
|
setTelemetryEnabled(value);
|
||||||
@ -26,7 +50,7 @@ const TosModal = () => {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("failed to set telemetry:", error);
|
console.error("failed to set telemetry:", error);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
services.FileService.GetFullConfig()
|
services.FileService.GetFullConfig()
|
||||||
@ -42,16 +66,16 @@ const TosModal = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const label = telemetryEnabled ? "Telemetry enabled" : "Telemetry disabled";
|
const label = telemetryEnabled ? "Telemetry Enabled" : "Telemetry Disabled";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlexiModal className="tos-modal">
|
<FlexiModal className="tos-modal" ref={modalRef}>
|
||||||
<div className="modal-inner">
|
<OverlayScrollbarsComponent className="modal-inner" options={{ scrollbars: { autoHide: "leave" } }}>
|
||||||
<header className="modal-header tos-header unselectable">
|
<header className="modal-header tos-header unselectable">
|
||||||
<div className="logo">
|
<div className="logo">
|
||||||
<Logo />
|
<Logo />
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-title">Welcome to Wave Terminal!</div>
|
<div className="modal-title">Welcome to Wave Terminal</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="modal-content tos-content unselectable">
|
<div className="modal-content tos-content unselectable">
|
||||||
<div className="content-section">
|
<div className="content-section">
|
||||||
@ -73,7 +97,7 @@ const TosModal = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="content-section">
|
<div className="content-section">
|
||||||
<div className="icon-wrapper">
|
<div className="icon-wrapper">
|
||||||
<a target="_blank" href="https://github.com/wavetermdev/thenextwave" rel={"noopener"}>
|
<a target="_blank" href="https://discord.gg/XfvZ334gwU" rel={"noopener"}>
|
||||||
<i className="icon fa-solid fa-people-group"></i>
|
<i className="icon fa-solid fa-people-group"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -96,15 +120,24 @@ const TosModal = () => {
|
|||||||
<div className="content-section-inner">
|
<div className="content-section-inner">
|
||||||
<div className="content-section-title">Telemetry</div>
|
<div className="content-section-title">Telemetry</div>
|
||||||
<div className="content-section-text">
|
<div className="content-section-text">
|
||||||
We collect minimal anonymous
|
We collect minimal anonymous{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://docs.waveterm.dev/reference/telemetry"
|
href="https://docs.waveterm.dev/reference/telemetry"
|
||||||
rel={"noopener"}
|
rel={"noopener"}
|
||||||
>
|
>
|
||||||
telemetry data
|
telemetry data
|
||||||
|
</a>{" "}
|
||||||
|
to help us understand how people are using Wave (
|
||||||
|
<a
|
||||||
|
className="plain-link"
|
||||||
|
target="_blank"
|
||||||
|
href="https://waveterm.dev/privacy"
|
||||||
|
rel="noopener"
|
||||||
|
>
|
||||||
|
Privacy Policy
|
||||||
</a>
|
</a>
|
||||||
to help us understand how people are using Wave.
|
).
|
||||||
</div>
|
</div>
|
||||||
<Toggle checked={telemetryEnabled} onChange={setTelemetry} label={label} />
|
<Toggle checked={telemetryEnabled} onChange={setTelemetry} label={label} />
|
||||||
</div>
|
</div>
|
||||||
@ -116,12 +149,8 @@ const TosModal = () => {
|
|||||||
Get Started
|
Get Started
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="content-section-text">
|
|
||||||
By continuing, I accept the
|
|
||||||
<a href="https://www.waveterm.dev/tos">Terms of Service</a>
|
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</OverlayScrollbarsComponent>
|
||||||
</FlexiModal>
|
</FlexiModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user