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);
|
||||
}
|
||||
|
||||
a.plain-link {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
|
@ -3,10 +3,65 @@
|
||||
|
||||
import { Button } from "@/app/element/button";
|
||||
import clsx from "clsx";
|
||||
import { forwardRef } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
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 {
|
||||
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 {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
onClickBackdrop?: () => void;
|
||||
}
|
||||
|
||||
const FlexiModal = ({ children, className, onClickBackdrop }: FlexiModalProps) => {
|
||||
const renderBackdrop = (onClick) => <div className="modal-backdrop" onClick={onClick}></div>;
|
||||
interface FlexiModalComponent
|
||||
extends React.ForwardRefExoticComponent<FlexiModalProps & React.RefAttributes<HTMLDivElement>> {
|
||||
Content: typeof ModalContent;
|
||||
Footer: typeof ModalFooter;
|
||||
}
|
||||
|
||||
const renderModal = () => (
|
||||
<div className="modal-wrapper">
|
||||
{renderBackdrop(onClickBackdrop)}
|
||||
<div className={`modal ${className}`}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
const FlexiModal = forwardRef<HTMLDivElement, FlexiModalProps>(
|
||||
({ children, className, onClickBackdrop }: FlexiModalProps, ref) => {
|
||||
const renderBackdrop = (onClick: () => void) => <div className="modal-backdrop" onClick={onClick}></div>;
|
||||
|
||||
return ReactDOM.createPortal(renderModal(), document.getElementById("main"));
|
||||
};
|
||||
const renderModal = () => (
|
||||
<div className="modal-wrapper">
|
||||
{renderBackdrop(onClickBackdrop)}
|
||||
<div className={`modal ${className}`} ref={ref}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
FlexiModal.Content = ModalContent;
|
||||
FlexiModal.Footer = ModalFooter;
|
||||
return ReactDOM.createPortal(renderModal(), document.getElementById("main")!);
|
||||
}
|
||||
);
|
||||
|
||||
(FlexiModal as FlexiModalComponent).Content = ModalContent;
|
||||
(FlexiModal as FlexiModalComponent).Footer = ModalFooter;
|
||||
|
||||
export { FlexiModal, Modal };
|
||||
|
@ -2,21 +2,22 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.tos-modal {
|
||||
width: 640px;
|
||||
width: 560px;
|
||||
border-radius: 10px;
|
||||
padding: 0;
|
||||
|
||||
.modal-inner {
|
||||
padding: 40px 76px;
|
||||
gap: 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
padding: 30px;
|
||||
|
||||
header.tos-header {
|
||||
flex-direction: column;
|
||||
gap: var(--sizing-sm, 12px);
|
||||
gap: 8px;
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 36px;
|
||||
|
||||
.logo {
|
||||
margin-bottom: 10px;
|
||||
@ -48,12 +49,17 @@
|
||||
gap: 32px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.check-toggle-wrapper .toggle-label {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.content-section {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
gap: 18px;
|
||||
|
||||
.icon-wrapper {
|
||||
.icon {
|
||||
@ -82,9 +88,13 @@
|
||||
}
|
||||
|
||||
.content-section-text {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
color: var(--secondary-text-color);
|
||||
font-style: normal;
|
||||
line-height: 20px;
|
||||
|
||||
b {
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.content-section-field {
|
||||
@ -102,10 +112,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
@ -123,7 +129,6 @@
|
||||
|
||||
button {
|
||||
font-size: 14px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
button.disabled-button {
|
||||
|
@ -6,19 +6,43 @@ import { Button } from "@/app/element/button";
|
||||
import { Toggle } from "@/app/element/toggle";
|
||||
import { WshServer } from "@/app/store/wshserver";
|
||||
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 "./tos.less";
|
||||
|
||||
const TosModal = () => {
|
||||
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 = () => {
|
||||
services.ClientService.AgreeTos();
|
||||
};
|
||||
|
||||
function setTelemetry(value: boolean) {
|
||||
const setTelemetry = (value: boolean) => {
|
||||
WshServer.SetConfigCommand({ "telemetry:enabled": value })
|
||||
.then(() => {
|
||||
setTelemetryEnabled(value);
|
||||
@ -26,7 +50,7 @@ const TosModal = () => {
|
||||
.catch((error) => {
|
||||
console.error("failed to set telemetry:", error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
services.FileService.GetFullConfig()
|
||||
@ -42,16 +66,16 @@ const TosModal = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const label = telemetryEnabled ? "Telemetry enabled" : "Telemetry disabled";
|
||||
const label = telemetryEnabled ? "Telemetry Enabled" : "Telemetry Disabled";
|
||||
|
||||
return (
|
||||
<FlexiModal className="tos-modal">
|
||||
<div className="modal-inner">
|
||||
<FlexiModal className="tos-modal" ref={modalRef}>
|
||||
<OverlayScrollbarsComponent className="modal-inner" options={{ scrollbars: { autoHide: "leave" } }}>
|
||||
<header className="modal-header tos-header unselectable">
|
||||
<div className="logo">
|
||||
<Logo />
|
||||
</div>
|
||||
<div className="modal-title">Welcome to Wave Terminal!</div>
|
||||
<div className="modal-title">Welcome to Wave Terminal</div>
|
||||
</header>
|
||||
<div className="modal-content tos-content unselectable">
|
||||
<div className="content-section">
|
||||
@ -73,7 +97,7 @@ const TosModal = () => {
|
||||
</div>
|
||||
<div className="content-section">
|
||||
<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>
|
||||
</a>
|
||||
</div>
|
||||
@ -96,15 +120,24 @@ const TosModal = () => {
|
||||
<div className="content-section-inner">
|
||||
<div className="content-section-title">Telemetry</div>
|
||||
<div className="content-section-text">
|
||||
We collect minimal anonymous
|
||||
We collect minimal anonymous{" "}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://docs.waveterm.dev/reference/telemetry"
|
||||
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>
|
||||
to help us understand how people are using Wave.
|
||||
).
|
||||
</div>
|
||||
<Toggle checked={telemetryEnabled} onChange={setTelemetry} label={label} />
|
||||
</div>
|
||||
@ -116,12 +149,8 @@ const TosModal = () => {
|
||||
Get Started
|
||||
</Button>
|
||||
</div>
|
||||
<div className="content-section-text">
|
||||
By continuing, I accept the
|
||||
<a href="https://www.waveterm.dev/tos">Terms of Service</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</FlexiModal>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user