mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-24 00:38:31 +08:00
Merge pull request #2624 from robintown/compound-typography
Replace typography components with Compound components
This commit is contained in:
commit
e699cb6411
@ -8,14 +8,20 @@ Please see LICENSE in the repository root for full details.
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
|
|
||||||
import { Link } from "../typography/Typography";
|
import { ExternalLink } from "../button/Link";
|
||||||
|
|
||||||
export const AnalyticsNotice: FC = () => (
|
export const AnalyticsNotice: FC = () => (
|
||||||
<Trans i18nKey="analytics_notice">
|
<Trans i18nKey="analytics_notice">
|
||||||
By participating in this beta, you consent to the collection of anonymous
|
By participating in this beta, you consent to the collection of anonymous
|
||||||
data, which we use to improve the product. You can find more information
|
data, which we use to improve the product. You can find more information
|
||||||
about which data we track in our{" "}
|
about which data we track in our{" "}
|
||||||
<Link href="https://element.io/privacy">Privacy Policy</Link> and our{" "}
|
<ExternalLink href="https://element.io/privacy">
|
||||||
<Link href="https://element.io/cookie-policy">Cookie Policy</Link>.
|
Privacy Policy
|
||||||
|
</ExternalLink>{" "}
|
||||||
|
and our{" "}
|
||||||
|
<ExternalLink href="https://element.io/cookie-policy">
|
||||||
|
Cookie Policy
|
||||||
|
</ExternalLink>
|
||||||
|
.
|
||||||
</Trans>
|
</Trans>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,7 @@ import { captureException } from "@sentry/react";
|
|||||||
import { sleep } from "matrix-js-sdk/src/utils";
|
import { sleep } from "matrix-js-sdk/src/utils";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button, Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||||
import { useClientLegacy } from "../ClientContext";
|
import { useClientLegacy } from "../ClientContext";
|
||||||
@ -28,10 +28,10 @@ import styles from "./LoginPage.module.css";
|
|||||||
import Logo from "../icons/LogoLarge.svg?react";
|
import Logo from "../icons/LogoLarge.svg?react";
|
||||||
import { LoadingView } from "../FullScreenView";
|
import { LoadingView } from "../FullScreenView";
|
||||||
import { useRecaptcha } from "./useRecaptcha";
|
import { useRecaptcha } from "./useRecaptcha";
|
||||||
import { Caption, Link } from "../typography/Typography";
|
|
||||||
import { usePageTitle } from "../usePageTitle";
|
import { usePageTitle } from "../usePageTitle";
|
||||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||||
import { Config } from "../config/Config";
|
import { Config } from "../config/Config";
|
||||||
|
import { ExternalLink, Link } from "../button/Link";
|
||||||
|
|
||||||
export const RegisterPage: FC = () => {
|
export const RegisterPage: FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -201,24 +201,24 @@ export const RegisterPage: FC = () => {
|
|||||||
data-testid="register_confirm_password"
|
data-testid="register_confirm_password"
|
||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
<Caption>
|
<Text size="sm">
|
||||||
<Trans i18nKey="recaptcha_caption">
|
<Trans i18nKey="recaptcha_caption">
|
||||||
This site is protected by ReCAPTCHA and the Google{" "}
|
This site is protected by ReCAPTCHA and the Google{" "}
|
||||||
<Link href="https://www.google.com/policies/privacy/">
|
<ExternalLink href="https://www.google.com/policies/privacy/">
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
</Link>{" "}
|
</ExternalLink>{" "}
|
||||||
and{" "}
|
and{" "}
|
||||||
<Link href="https://policies.google.com/terms">
|
<ExternalLink href="https://policies.google.com/terms">
|
||||||
Terms of Service
|
Terms of Service
|
||||||
</Link>{" "}
|
</ExternalLink>{" "}
|
||||||
apply.
|
apply.
|
||||||
<br />
|
<br />
|
||||||
By clicking "Register", you agree to our{" "}
|
By clicking "Register", you agree to our{" "}
|
||||||
<Link href={Config.get().eula}>
|
<ExternalLink href={Config.get().eula}>
|
||||||
End User Licensing Agreement (EULA)
|
End User Licensing Agreement (EULA)
|
||||||
</Link>
|
</ExternalLink>
|
||||||
</Trans>
|
</Trans>
|
||||||
</Caption>
|
</Text>
|
||||||
{error && (
|
{error && (
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<ErrorMessage error={error} />
|
<ErrorMessage error={error} />
|
||||||
|
13
src/button/Link.module.css
Normal file
13
src/button/Link.module.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.external {
|
||||||
|
/* By default links will be blue/purple (or whatever the user agent does), but
|
||||||
|
in our designs we generally want external links to be the same color as the
|
||||||
|
surrounding text */
|
||||||
|
color: inherit;
|
||||||
|
}
|
@ -15,10 +15,16 @@ import {
|
|||||||
import { Link as CpdLink } from "@vector-im/compound-web";
|
import { Link as CpdLink } from "@vector-im/compound-web";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { createPath, LocationDescriptor, Path } from "history";
|
import { createPath, LocationDescriptor, Path } from "history";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
import { useLatest } from "../useLatest";
|
||||||
|
import styles from "./Link.module.css";
|
||||||
|
|
||||||
export function useLink(
|
export function useLink(
|
||||||
to: LocationDescriptor,
|
to: LocationDescriptor,
|
||||||
|
state?: unknown,
|
||||||
): [Path, (e: MouseEvent) => void] {
|
): [Path, (e: MouseEvent) => void] {
|
||||||
|
const latestState = useLatest(state);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const path = useMemo(
|
const path = useMemo(
|
||||||
() => (typeof to === "string" ? to : createPath(to)),
|
() => (typeof to === "string" ? to : createPath(to)),
|
||||||
@ -27,9 +33,9 @@ export function useLink(
|
|||||||
const onClick = useCallback(
|
const onClick = useCallback(
|
||||||
(e: MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
history.push(to);
|
history.push(to, latestState.current);
|
||||||
},
|
},
|
||||||
[history, to],
|
[history, to, latestState],
|
||||||
);
|
);
|
||||||
|
|
||||||
return [path, onClick];
|
return [path, onClick];
|
||||||
@ -38,15 +44,37 @@ export function useLink(
|
|||||||
type Props = Omit<
|
type Props = Omit<
|
||||||
ComponentPropsWithoutRef<typeof CpdLink>,
|
ComponentPropsWithoutRef<typeof CpdLink>,
|
||||||
"href" | "onClick"
|
"href" | "onClick"
|
||||||
> & { to: LocationDescriptor };
|
> & { to: LocationDescriptor; state?: unknown };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A version of Compound's link component that integrates with our router setup.
|
* A version of Compound's link component that integrates with our router setup.
|
||||||
|
* This is only for app-internal links.
|
||||||
*/
|
*/
|
||||||
export const Link = forwardRef<HTMLAnchorElement, Props>(function Link(
|
export const Link = forwardRef<HTMLAnchorElement, Props>(function Link(
|
||||||
{ to, ...props },
|
{ to, state, ...props },
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const [path, onClick] = useLink(to);
|
const [path, onClick] = useLink(to, state);
|
||||||
return <CpdLink ref={ref} {...props} href={path} onClick={onClick} />;
|
return <CpdLink ref={ref} {...props} href={path} onClick={onClick} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A link to an external web page, made to fit into blocks of text more subtly
|
||||||
|
* than the normal Compound link component.
|
||||||
|
*/
|
||||||
|
export const ExternalLink = forwardRef<
|
||||||
|
HTMLAnchorElement,
|
||||||
|
ComponentPropsWithoutRef<"a">
|
||||||
|
>(function ExternalLink({ className, children, ...props }, ref) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
ref={ref}
|
||||||
|
className={classNames(className, styles.external)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
@ -50,6 +50,12 @@ Please see LICENSE in the repository root for full details.
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.callName {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.facePile {
|
.facePile {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,11 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
|
|||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
|
import { Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { Avatar, Size } from "../Avatar";
|
import { Avatar, Size } from "../Avatar";
|
||||||
import styles from "./CallList.module.css";
|
import styles from "./CallList.module.css";
|
||||||
import { getRelativeRoomUrl } from "../utils/matrix";
|
import { getRelativeRoomUrl } from "../utils/matrix";
|
||||||
import { Body } from "../typography/Typography";
|
|
||||||
import { GroupCallRoom } from "./useGroupCallRooms";
|
import { GroupCallRoom } from "./useGroupCallRooms";
|
||||||
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement";
|
import { useRoomEncryptionSystem } from "../e2ee/sharedKeyManagement";
|
||||||
|
|
||||||
@ -65,9 +65,9 @@ const CallTile: FC<CallTileProps> = ({ name, avatarUrl, room }) => {
|
|||||||
>
|
>
|
||||||
<Avatar id={room.roomId} name={name} size={Size.LG} src={avatarUrl} />
|
<Avatar id={room.roomId} name={name} size={Size.LG} src={avatarUrl} />
|
||||||
<div className={styles.callInfo}>
|
<div className={styles.callInfo}>
|
||||||
<Body overflowEllipsis fontWeight="semiBold">
|
<Text weight="semibold" className={styles.callName}>
|
||||||
{name}
|
{name}
|
||||||
</Body>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.copyButtonSpacer} />
|
<div className={styles.copyButtonSpacer} />
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -9,7 +9,7 @@ import { useState, useCallback, FormEvent, FormEventHandler, FC } from "react";
|
|||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Heading } from "@vector-im/compound-web";
|
import { Heading, Text } from "@vector-im/compound-web";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button } from "@vector-im/compound-web";
|
||||||
|
|
||||||
@ -27,7 +27,6 @@ import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
|||||||
import { CallList } from "./CallList";
|
import { CallList } from "./CallList";
|
||||||
import { UserMenuContainer } from "../UserMenuContainer";
|
import { UserMenuContainer } from "../UserMenuContainer";
|
||||||
import { JoinExistingCallModal } from "./JoinExistingCallModal";
|
import { JoinExistingCallModal } from "./JoinExistingCallModal";
|
||||||
import { Caption } from "../typography/Typography";
|
|
||||||
import { Form } from "../form/Form";
|
import { Form } from "../form/Form";
|
||||||
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
|
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
|
||||||
import { E2eeType } from "../e2ee/e2eeType";
|
import { E2eeType } from "../e2ee/e2eeType";
|
||||||
@ -144,9 +143,9 @@ export const RegisteredView: FC<Props> = ({ client }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
{optInAnalytics === null && (
|
{optInAnalytics === null && (
|
||||||
<Caption className={styles.notice}>
|
<Text size="sm" className={styles.notice}>
|
||||||
<AnalyticsNotice />
|
<AnalyticsNotice />
|
||||||
</Caption>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
<FieldRow className={styles.fieldRow}>
|
<FieldRow className={styles.fieldRow}>
|
||||||
|
@ -9,7 +9,7 @@ import { FC, useCallback, useState, FormEventHandler } from "react";
|
|||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { Button, Heading } from "@vector-im/compound-web";
|
import { Button, Heading, Text } from "@vector-im/compound-web";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { useClient } from "../ClientContext";
|
import { useClient } from "../ClientContext";
|
||||||
@ -25,7 +25,6 @@ import {
|
|||||||
import { useInteractiveRegistration } from "../auth/useInteractiveRegistration";
|
import { useInteractiveRegistration } from "../auth/useInteractiveRegistration";
|
||||||
import { JoinExistingCallModal } from "./JoinExistingCallModal";
|
import { JoinExistingCallModal } from "./JoinExistingCallModal";
|
||||||
import { useRecaptcha } from "../auth/useRecaptcha";
|
import { useRecaptcha } from "../auth/useRecaptcha";
|
||||||
import { Body, Caption, Link } from "../typography/Typography";
|
|
||||||
import { Form } from "../form/Form";
|
import { Form } from "../form/Form";
|
||||||
import styles from "./UnauthenticatedView.module.css";
|
import styles from "./UnauthenticatedView.module.css";
|
||||||
import commonStyles from "./common.module.css";
|
import commonStyles from "./common.module.css";
|
||||||
@ -34,6 +33,7 @@ import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
|
|||||||
import { Config } from "../config/Config";
|
import { Config } from "../config/Config";
|
||||||
import { E2eeType } from "../e2ee/e2eeType";
|
import { E2eeType } from "../e2ee/e2eeType";
|
||||||
import { useOptInAnalytics } from "../settings/settings";
|
import { useOptInAnalytics } from "../settings/settings";
|
||||||
|
import { ExternalLink, Link } from "../button/Link";
|
||||||
|
|
||||||
export const UnauthenticatedView: FC = () => {
|
export const UnauthenticatedView: FC = () => {
|
||||||
const { setClient } = useClient();
|
const { setClient } = useClient();
|
||||||
@ -178,18 +178,18 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
{optInAnalytics === null && (
|
{optInAnalytics === null && (
|
||||||
<Caption className={styles.notice}>
|
<Text size="sm" className={styles.notice}>
|
||||||
<AnalyticsNotice />
|
<AnalyticsNotice />
|
||||||
</Caption>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Caption className={styles.notice}>
|
<Text size="sm" className={styles.notice}>
|
||||||
<Trans i18nKey="unauthenticated_view_eula_caption">
|
<Trans i18nKey="unauthenticated_view_eula_caption">
|
||||||
By clicking "Go", you agree to our{" "}
|
By clicking "Go", you agree to our{" "}
|
||||||
<Link href={Config.get().eula}>
|
<ExternalLink href={Config.get().eula}>
|
||||||
End User Licensing Agreement (EULA)
|
End User Licensing Agreement (EULA)
|
||||||
</Link>
|
</ExternalLink>
|
||||||
</Trans>
|
</Trans>
|
||||||
</Caption>
|
</Text>
|
||||||
{error && (
|
{error && (
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<ErrorMessage error={error} />
|
<ErrorMessage error={error} />
|
||||||
@ -207,19 +207,19 @@ export const UnauthenticatedView: FC = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
</main>
|
</main>
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<Body className={styles.mobileLoginLink}>
|
<Text className={styles.mobileLoginLink}>
|
||||||
<Link color="primary" to="/login" data-testid="home_login">
|
<Link to="/login" data-testid="home_login">
|
||||||
{t("unauthenticated_view_login_button")}
|
{t("unauthenticated_view_login_button")}
|
||||||
</Link>
|
</Link>
|
||||||
</Body>
|
</Text>
|
||||||
<Body>
|
<Text>
|
||||||
<Trans i18nKey="unauthenticated_view_body">
|
<Trans i18nKey="unauthenticated_view_body">
|
||||||
Not registered yet?{" "}
|
Not registered yet?{" "}
|
||||||
<Link color="primary" to="/register" data-testid="home_register">
|
<Link to="/register" data-testid="home_register">
|
||||||
Create an account
|
Create an account
|
||||||
</Link>
|
</Link>
|
||||||
</Trans>
|
</Trans>
|
||||||
</Body>
|
</Text>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
{onFinished && (
|
{onFinished && (
|
||||||
|
@ -237,16 +237,6 @@ body[data-platform="desktop"] {
|
|||||||
line-height: var(--font-size-title);
|
line-height: var(--font-size-title);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--cpd-color-text-action-accent);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:active {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
width: calc(100% - 24px);
|
width: calc(100% - 24px);
|
||||||
border: none;
|
border: none;
|
||||||
|
@ -9,12 +9,11 @@ import { FC, FormEventHandler, ReactNode, useCallback, useState } from "react";
|
|||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button, Heading, Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import styles from "./CallEndedView.module.css";
|
import styles from "./CallEndedView.module.css";
|
||||||
import feedbackStyle from "../input/FeedbackInput.module.css";
|
import feedbackStyle from "../input/FeedbackInput.module.css";
|
||||||
import { useProfile } from "../profile/useProfile";
|
import { useProfile } from "../profile/useProfile";
|
||||||
import { Body, Headline } from "../typography/Typography";
|
|
||||||
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
|
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
|
||||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||||
import { FieldRow, InputField } from "../input/Input";
|
import { FieldRow, InputField } from "../input/Input";
|
||||||
@ -139,11 +138,11 @@ export const CallEndedView: FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<Headline className={styles.headline}>
|
<Heading size="xl" weight="semibold" className={styles.headline}>
|
||||||
<Trans i18nKey="call_ended_view.body">
|
<Trans i18nKey="call_ended_view.body">
|
||||||
You were disconnected from the call
|
You were disconnected from the call
|
||||||
</Trans>
|
</Trans>
|
||||||
</Headline>
|
</Heading>
|
||||||
<div className={styles.disconnectedButtons}>
|
<div className={styles.disconnectedButtons}>
|
||||||
<Button onClick={reconnect}>
|
<Button onClick={reconnect}>
|
||||||
{t("call_ended_view.reconnect_button")}
|
{t("call_ended_view.reconnect_button")}
|
||||||
@ -154,9 +153,9 @@ export const CallEndedView: FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{!confineToRoom && (
|
{!confineToRoom && (
|
||||||
<Body className={styles.footer}>
|
<Text className={styles.footer}>
|
||||||
<Link to="/"> {t("return_home_button")} </Link>
|
<Link to="/"> {t("return_home_button")} </Link>
|
||||||
</Body>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -164,7 +163,7 @@ export const CallEndedView: FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<Headline className={styles.headline}>
|
<Heading size="xl" weight="semibold" className={styles.headline}>
|
||||||
{surveySubmitted
|
{surveySubmitted
|
||||||
? t("call_ended_view.headline", {
|
? t("call_ended_view.headline", {
|
||||||
displayName,
|
displayName,
|
||||||
@ -174,16 +173,16 @@ export const CallEndedView: FC<Props> = ({
|
|||||||
}) +
|
}) +
|
||||||
"\n" +
|
"\n" +
|
||||||
t("call_ended_view.survey_prompt")}
|
t("call_ended_view.survey_prompt")}
|
||||||
</Headline>
|
</Heading>
|
||||||
{(!surveySubmitted || confineToRoom) &&
|
{(!surveySubmitted || confineToRoom) &&
|
||||||
PosthogAnalytics.instance.isEnabled()
|
PosthogAnalytics.instance.isEnabled()
|
||||||
? qualitySurveyDialog
|
? qualitySurveyDialog
|
||||||
: createAccountDialog}
|
: createAccountDialog}
|
||||||
</main>
|
</main>
|
||||||
{!confineToRoom && (
|
{!confineToRoom && (
|
||||||
<Body className={styles.footer}>
|
<Text className={styles.footer}>
|
||||||
<Link to="/"> {t("call_ended_view.not_now_button")} </Link>
|
<Link to="/"> {t("call_ended_view.not_now_button")} </Link>
|
||||||
</Body>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -5,13 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useCallback } from "react";
|
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { MatrixError } from "matrix-js-sdk/src/matrix";
|
import { MatrixError } from "matrix-js-sdk/src/matrix";
|
||||||
import { useHistory } from "react-router-dom";
|
import { Heading, Text } from "@vector-im/compound-web";
|
||||||
import { Heading, Link, Text } from "@vector-im/compound-web";
|
|
||||||
|
|
||||||
|
import { Link } from "../button/Link";
|
||||||
import {
|
import {
|
||||||
useLoadGroupCall,
|
useLoadGroupCall,
|
||||||
GroupCallStatus,
|
GroupCallStatus,
|
||||||
@ -35,15 +34,6 @@ export function GroupCallLoader({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const groupCallState = useLoadGroupCall(client, roomIdOrAlias, viaServers);
|
const groupCallState = useLoadGroupCall(client, roomIdOrAlias, viaServers);
|
||||||
|
|
||||||
const history = useHistory();
|
|
||||||
const onHomeClick = useCallback(
|
|
||||||
(ev: React.MouseEvent) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
history.push("/");
|
|
||||||
},
|
|
||||||
[history],
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (groupCallState.kind) {
|
switch (groupCallState.kind) {
|
||||||
case "loaded":
|
case "loaded":
|
||||||
case "waitForInvite":
|
case "waitForInvite":
|
||||||
@ -63,9 +53,7 @@ export function GroupCallLoader({
|
|||||||
<Text>{t("group_call_loader.failed_text")}</Text>
|
<Text>{t("group_call_loader.failed_text")}</Text>
|
||||||
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
|
{/* XXX: A 'create it for me' button would be the obvious UX here. Two screens already have
|
||||||
dupes of this flow, let's make a common component and put it here. */}
|
dupes of this flow, let's make a common component and put it here. */}
|
||||||
<Link href="/" onClick={onHomeClick}>
|
<Link to="/">{t("common.home")}</Link>
|
||||||
{t("common.home")}
|
|
||||||
</Link>
|
|
||||||
</FullScreenView>
|
</FullScreenView>
|
||||||
);
|
);
|
||||||
} else if (groupCallState.error instanceof CallTerminatedMessage) {
|
} else if (groupCallState.error instanceof CallTerminatedMessage) {
|
||||||
@ -79,9 +67,7 @@ export function GroupCallLoader({
|
|||||||
<Text size="sm">"{groupCallState.error.reason}"</Text>
|
<Text size="sm">"{groupCallState.error.reason}"</Text>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Link href="/" onClick={onHomeClick}>
|
<Link to="/">{t("common.home")}</Link>
|
||||||
{t("common.home")}
|
|
||||||
</Link>
|
|
||||||
</FullScreenView>
|
</FullScreenView>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc/MatrixRTCSession";
|
||||||
import { JoinRule } from "matrix-js-sdk/src/matrix";
|
import { JoinRule } from "matrix-js-sdk/src/matrix";
|
||||||
import { Heading, Link, Text } from "@vector-im/compound-web";
|
import { Heading, Text } from "@vector-im/compound-web";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { IWidgetApiRequest } from "matrix-widget-api";
|
import type { IWidgetApiRequest } from "matrix-widget-api";
|
||||||
@ -40,6 +40,7 @@ import { useJoinRule } from "./useJoinRule";
|
|||||||
import { InviteModal } from "./InviteModal";
|
import { InviteModal } from "./InviteModal";
|
||||||
import { useUrlParams } from "../UrlParams";
|
import { useUrlParams } from "../UrlParams";
|
||||||
import { E2eeType } from "../e2ee/e2eeType";
|
import { E2eeType } from "../e2ee/e2eeType";
|
||||||
|
import { Link } from "../button/Link";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -281,14 +282,6 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
);
|
);
|
||||||
const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null;
|
const onShareClick = joinRule === JoinRule.Public ? onShareClickFn : null;
|
||||||
|
|
||||||
const onHomeClick = useCallback(
|
|
||||||
(ev: React.MouseEvent) => {
|
|
||||||
ev.preventDefault();
|
|
||||||
history.push("/");
|
|
||||||
},
|
|
||||||
[history],
|
|
||||||
);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (!isE2EESupportedBrowser() && e2eeSystem.kind !== E2eeType.NONE) {
|
if (!isE2EESupportedBrowser() && e2eeSystem.kind !== E2eeType.NONE) {
|
||||||
@ -297,9 +290,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
<FullScreenView>
|
<FullScreenView>
|
||||||
<Heading>{t("browser_media_e2ee_unsupported_heading")}</Heading>
|
<Heading>{t("browser_media_e2ee_unsupported_heading")}</Heading>
|
||||||
<Text>{t("browser_media_e2ee_unsupported")}</Text>
|
<Text>{t("browser_media_e2ee_unsupported")}</Text>
|
||||||
<Link href="/" onClick={onHomeClick}>
|
<Link to="/">{t("common.home")}</Link>
|
||||||
{t("common.home")}
|
|
||||||
</Link>
|
|
||||||
</FullScreenView>
|
</FullScreenView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,11 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import { FC, useEffect } from "react";
|
import { FC, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button, Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { Modal, Props as ModalProps } from "../Modal";
|
import { Modal, Props as ModalProps } from "../Modal";
|
||||||
import { FieldRow, ErrorMessage } from "../input/Input";
|
import { FieldRow, ErrorMessage } from "../input/Input";
|
||||||
import { useSubmitRageshake } from "../settings/submit-rageshake";
|
import { useSubmitRageshake } from "../settings/submit-rageshake";
|
||||||
import { Body } from "../typography/Typography";
|
|
||||||
|
|
||||||
interface Props extends Omit<ModalProps, "title" | "children"> {
|
interface Props extends Omit<ModalProps, "title" | "children"> {
|
||||||
rageshakeRequestId: string;
|
rageshakeRequestId: string;
|
||||||
@ -40,7 +39,7 @@ export const RageshakeRequestModal: FC<Props> = ({
|
|||||||
open={open}
|
open={open}
|
||||||
onDismiss={onDismiss}
|
onDismiss={onDismiss}
|
||||||
>
|
>
|
||||||
<Body>{t("rageshake_request_modal.body")}</Body>
|
<Text>{t("rageshake_request_modal.body")}</Text>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<Button
|
<Button
|
||||||
onClick={(): void =>
|
onClick={(): void =>
|
||||||
|
@ -9,16 +9,16 @@ import { FC, useCallback, useState } from "react";
|
|||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button, Heading, Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import styles from "./RoomAuthView.module.css";
|
import styles from "./RoomAuthView.module.css";
|
||||||
import { Body, Caption, Link, Headline } from "../typography/Typography";
|
|
||||||
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
|
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
|
||||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||||
import { Form } from "../form/Form";
|
import { Form } from "../form/Form";
|
||||||
import { UserMenuContainer } from "../UserMenuContainer";
|
import { UserMenuContainer } from "../UserMenuContainer";
|
||||||
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
|
||||||
import { Config } from "../config/Config";
|
import { Config } from "../config/Config";
|
||||||
|
import { ExternalLink, Link } from "../button/Link";
|
||||||
|
|
||||||
export const RoomAuthView: FC = () => {
|
export const RoomAuthView: FC = () => {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -63,9 +63,9 @@ export const RoomAuthView: FC = () => {
|
|||||||
</Header>
|
</Header>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<Headline className={styles.headline}>
|
<Heading size="xl" weight="semibold" className={styles.headline}>
|
||||||
{t("lobby.join_button")}
|
{t("lobby.join_button")}
|
||||||
</Headline>
|
</Heading>
|
||||||
<Form className={styles.form} onSubmit={onSubmit}>
|
<Form className={styles.form} onSubmit={onSubmit}>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<InputField
|
<InputField
|
||||||
@ -79,14 +79,14 @@ export const RoomAuthView: FC = () => {
|
|||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
<Caption>
|
<Text size="sm">
|
||||||
<Trans i18nKey="room_auth_view_eula_caption">
|
<Trans i18nKey="room_auth_view_eula_caption">
|
||||||
By clicking "Join call now", you agree to our{" "}
|
By clicking "Join call now", you agree to our{" "}
|
||||||
<Link href={Config.get().eula}>
|
<ExternalLink href={Config.get().eula}>
|
||||||
End User Licensing Agreement (EULA)
|
End User Licensing Agreement (EULA)
|
||||||
</Link>
|
</ExternalLink>
|
||||||
</Trans>
|
</Trans>
|
||||||
</Caption>
|
</Text>
|
||||||
{error && (
|
{error && (
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<ErrorMessage error={error} />
|
<ErrorMessage error={error} />
|
||||||
@ -103,17 +103,14 @@ export const RoomAuthView: FC = () => {
|
|||||||
<div id={recaptchaId} />
|
<div id={recaptchaId} />
|
||||||
</Form>
|
</Form>
|
||||||
</main>
|
</main>
|
||||||
<Body className={styles.footer}>
|
<Text className={styles.footer}>
|
||||||
<Trans i18nKey="unauthenticated_view_body">
|
<Trans i18nKey="unauthenticated_view_body">
|
||||||
Not registered yet?{" "}
|
Not registered yet?{" "}
|
||||||
<Link
|
<Link to="/register" state={{ from: location }}>
|
||||||
color="primary"
|
|
||||||
to={{ pathname: "/register", state: { from: location } }}
|
|
||||||
>
|
|
||||||
Create an account
|
Create an account
|
||||||
</Link>
|
</Link>
|
||||||
</Trans>
|
</Trans>
|
||||||
</Body>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -8,12 +8,11 @@ Please see LICENSE in the repository root for full details.
|
|||||||
import { FC, useCallback } from "react";
|
import { FC, useCallback } from "react";
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button } from "@vector-im/compound-web";
|
import { Button, Text } from "@vector-im/compound-web";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||||
import { useSubmitRageshake, useRageshakeRequest } from "./submit-rageshake";
|
import { useSubmitRageshake, useRageshakeRequest } from "./submit-rageshake";
|
||||||
import { Body } from "../typography/Typography";
|
|
||||||
import feedbackStyles from "../input/FeedbackInput.module.css";
|
import feedbackStyles from "../input/FeedbackInput.module.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -56,7 +55,7 @@ export const FeedbackSettingsTab: FC<Props> = ({ roomId }) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h4>{t("settings.feedback_tab_h4")}</h4>
|
<h4>{t("settings.feedback_tab_h4")}</h4>
|
||||||
<Body>{t("settings.feedback_tab_body")}</Body>
|
<Text>{t("settings.feedback_tab_body")}</Text>
|
||||||
<form onSubmit={onSubmitFeedback}>
|
<form onSubmit={onSubmitFeedback}>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<InputField
|
<InputField
|
||||||
@ -85,7 +84,7 @@ export const FeedbackSettingsTab: FC<Props> = ({ roomId }) => {
|
|||||||
)}
|
)}
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
{error && <ErrorMessage error={error} />}
|
{error && <ErrorMessage error={error} />}
|
||||||
{sent && <Body> {t("settings.feedback_tab_thank_you")}</Body>}
|
{sent && <Text>{t("settings.feedback_tab_thank_you")}</Text>}
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,13 +8,12 @@ Please see LICENSE in the repository root for full details.
|
|||||||
import { ChangeEvent, FC, ReactNode, useCallback } from "react";
|
import { ChangeEvent, FC, ReactNode, useCallback } from "react";
|
||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
import { Dropdown } from "@vector-im/compound-web";
|
import { Dropdown, Text } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { Modal } from "../Modal";
|
import { Modal } from "../Modal";
|
||||||
import styles from "./SettingsModal.module.css";
|
import styles from "./SettingsModal.module.css";
|
||||||
import { Tab, TabContainer } from "../tabs/Tabs";
|
import { Tab, TabContainer } from "../tabs/Tabs";
|
||||||
import { FieldRow, InputField } from "../input/Input";
|
import { FieldRow, InputField } from "../input/Input";
|
||||||
import { Caption } from "../typography/Typography";
|
|
||||||
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
|
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
|
||||||
import { ProfileSettingsTab } from "./ProfileSettingsTab";
|
import { ProfileSettingsTab } from "./ProfileSettingsTab";
|
||||||
import { FeedbackSettingsTab } from "./FeedbackSettingsTab";
|
import { FeedbackSettingsTab } from "./FeedbackSettingsTab";
|
||||||
@ -102,14 +101,14 @@ export const SettingsModal: FC<Props> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const optInDescription = (
|
const optInDescription = (
|
||||||
<Caption>
|
<Text size="sm">
|
||||||
<Trans i18nKey="settings.opt_in_description">
|
<Trans i18nKey="settings.opt_in_description">
|
||||||
<AnalyticsNotice />
|
<AnalyticsNotice />
|
||||||
<br />
|
<br />
|
||||||
You may withdraw consent by unchecking this box. If you are currently in
|
You may withdraw consent by unchecking this box. If you are currently in
|
||||||
a call, this setting will take effect at the end of the call.
|
a call, this setting will take effect at the end of the call.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Caption>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
||||||
const devices = useMediaDevices();
|
const devices = useMediaDevices();
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.caption {
|
|
||||||
font-size: var(--font-size-caption);
|
|
||||||
line-height: var(--font-size-body);
|
|
||||||
}
|
|
||||||
|
|
||||||
.micro {
|
|
||||||
font-size: var(--font-size-micro);
|
|
||||||
line-height: var(--font-size-caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
.regular {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semiBold {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bold {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: var(--cpd-color-text-link-external);
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
opacity: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary {
|
|
||||||
color: var(--cpd-color-text-action-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflowEllipsis {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
@ -1,262 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2022-2024 New Vector Ltd.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { createElement, forwardRef, ReactNode } from "react";
|
|
||||||
import classNames from "classnames";
|
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
|
||||||
import * as H from "history";
|
|
||||||
|
|
||||||
import styles from "./Typography.module.css";
|
|
||||||
|
|
||||||
interface TypographyProps {
|
|
||||||
children: ReactNode;
|
|
||||||
fontWeight?: string;
|
|
||||||
className?: string;
|
|
||||||
overflowEllipsis?: boolean;
|
|
||||||
as?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Headline = forwardRef<HTMLHeadingElement, TypographyProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as: Component = "h1",
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
className: classNames(
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Headline.displayName = "Headline";
|
|
||||||
|
|
||||||
export const Title = forwardRef<HTMLHeadingElement, TypographyProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as: Component = "h2",
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
className: classNames(
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Title.displayName = "Title";
|
|
||||||
|
|
||||||
export const Subtitle = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as: Component = "h3",
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
className: classNames(
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Subtitle.displayName = "Subtitle";
|
|
||||||
|
|
||||||
export const Body = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as: Component = "p",
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
className: classNames(
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Body.displayName = "Body";
|
|
||||||
|
|
||||||
export const Caption = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as: Component = "p",
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
className: classNames(
|
|
||||||
styles.caption,
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Caption.displayName = "Caption";
|
|
||||||
|
|
||||||
export const Micro = forwardRef<HTMLParagraphElement, TypographyProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as: Component = "p",
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
...rest,
|
|
||||||
className: classNames(
|
|
||||||
styles.micro,
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Micro.displayName = "Micro";
|
|
||||||
|
|
||||||
interface LinkProps extends TypographyProps {
|
|
||||||
to?: H.LocationDescriptor<unknown>;
|
|
||||||
color?: string;
|
|
||||||
href?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
as,
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
color = "link",
|
|
||||||
href,
|
|
||||||
to,
|
|
||||||
fontWeight,
|
|
||||||
overflowEllipsis,
|
|
||||||
...rest
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
const Component: string | RouterLink = as || (to ? RouterLink : "a");
|
|
||||||
let externalLinkProps: { href: string; target: string; rel: string };
|
|
||||||
|
|
||||||
if (href) {
|
|
||||||
externalLinkProps = {
|
|
||||||
href,
|
|
||||||
target: "_blank",
|
|
||||||
rel: "noreferrer noopener",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return createElement(
|
|
||||||
Component,
|
|
||||||
{
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
...externalLinkProps,
|
|
||||||
...rest,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
to: to,
|
|
||||||
className: classNames(
|
|
||||||
styles[color],
|
|
||||||
styles[fontWeight ?? ""],
|
|
||||||
{ [styles.overflowEllipsis]: overflowEllipsis },
|
|
||||||
className,
|
|
||||||
),
|
|
||||||
ref: ref,
|
|
||||||
},
|
|
||||||
children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Link.displayName = "Link";
|
|
Loading…
Reference in New Issue
Block a user