Switch to rendering svg icons rather than masking them (#31550)

* Switch to rendering svg icons rather than masking them in SpacePanel

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix badly rendered icon in JoinRuleDropdown

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix badly rendered icon in RoomPreviewCard

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix badly rendered icon in Space menus

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix badly rendered icon in ThreadPanel

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update snapshots

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove unused icon underfill

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update screenshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add missing snapshot

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-12-16 13:56:36 +00:00
committed by GitHub
parent 773662e018
commit 7e3a6d9c42
24 changed files with 645 additions and 319 deletions
@@ -0,0 +1,27 @@
/*
Copyright 2025 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX } from "react";
import AccessibleButton from "../views/elements/AccessibleButton";
const SpacePillButton: React.FC<{
title: string;
icon: JSX.Element;
description: string;
onClick(): void;
}> = ({ title, icon, description, onClick }) => {
return (
<AccessibleButton className="mx_SpacePillButton" onClick={onClick}>
{icon}
{title}
<div>{description}</div>
</AccessibleButton>
);
};
export default SpacePillButton;
+17 -13
View File
@@ -10,7 +10,12 @@ import { EventType, RoomType, JoinRule, Preset, type Room, RoomEvent } from "mat
import { KnownMembership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger";
import React, { type JSX, useCallback, useContext, useRef, useState } from "react";
import { PlusIcon, RoomIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import {
GroupIcon,
PlusIcon,
RoomIcon,
UserProfileSolidIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import createRoom, { type IOpts } from "../../createRoom";
@@ -68,6 +73,7 @@ import RightPanel from "./RightPanel";
import SpaceHierarchy, { showRoom } from "./SpaceHierarchy";
import { type RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import { Icon as HashVideoIcon } from "../../../res/img/element-icons/roomlist/hash-video.svg";
import SpacePillButton from "./SpacePillButton.tsx";
interface IProps {
space: Room;
@@ -455,24 +461,22 @@ const SpaceSetupPrivateScope: React.FC<{
})}
</div>
<AccessibleButton
className="mx_SpaceRoomView_privateScope_justMeButton"
<SpacePillButton
icon={<UserProfileSolidIcon />}
title={_t("create_space|personal_space")}
description={_t("create_space|personal_space_description")}
onClick={() => {
onFinished(false);
}}
>
{_t("create_space|personal_space")}
<div>{_t("create_space|personal_space_description")}</div>
</AccessibleButton>
<AccessibleButton
className="mx_SpaceRoomView_privateScope_meAndMyTeammatesButton"
/>
<SpacePillButton
icon={<GroupIcon />}
title={_t("create_space|private_space")}
description={_t("create_space|private_space_description")}
onClick={() => {
onFinished(true);
}}
>
{_t("create_space|private_space")}
<div>{_t("create_space|private_space_description")}</div>
</AccessibleButton>
/>
</div>
);
};
+3 -1
View File
@@ -10,7 +10,7 @@ import React, { useContext, useEffect, useRef, useState } from "react";
import { type EventTimelineSet, type Room, Thread } from "matrix-js-sdk/src/matrix";
import { IconButton, Tooltip } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";
import ThreadsIcon from "@vector-im/compound-design-tokens/assets/web/icons/threads";
import { ThreadsIcon, CheckIcon, ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { Icon as MarkAllThreadsReadIcon } from "../../../res/img/element-icons/check-all.svg";
import BaseCard from "../views/right_panel/BaseCard";
@@ -57,6 +57,7 @@ export const ThreadPanelHeaderFilterOptionItem: React.FC<
> = ({ label, description, onClick, isSelected }) => {
return (
<MenuItemRadio active={isSelected} className="mx_ThreadPanel_Header_FilterOptionItem" onClick={onClick}>
{isSelected ? <CheckIcon /> : null}
<span>{label}</span>
<span>{description}</span>
</MenuItemRadio>
@@ -145,6 +146,7 @@ export const ThreadPanelHeader: React.FC<{
}}
>
{`${_t("threads|show_thread_filter")} ${value?.label}`}
<ChevronDownIcon />
</ContextMenuButton>
{contextMenu}
</div>
@@ -57,7 +57,7 @@ const JoinRuleDropdown: React.FC<IProps> = ({
options.unshift(
(
<div key={JoinRule.Knock} className="mx_JoinRuleDropdown_knock">
<AskToJoinIcon className="mx_Icon mx_Icon_16 mx_JoinRuleDropdown_icon" />
<AskToJoinIcon />
{labelKnock}
</div>
) as ReactElement & { key: string },
@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, type FC, useContext, useState } from "react";
import { type Room, JoinRule } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { InfoSolidIcon, VideoCallSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import defaultDispatcher from "../../../dispatcher/dispatcher";
@@ -152,7 +153,9 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
avatarRow = (
<>
<RoomAvatar room={room} size="50px" viewAvatarOnClick />
<div className="mx_RoomPreviewCard_video" />
<div className="mx_RoomPreviewCard_video">
<VideoCallSolidIcon />
</div>
<BetaPill onClick={viewLabs} tooltipTitle={_t("labs|video_rooms_beta")} />
</>
);
@@ -174,6 +177,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
{room.getJoinRule() === "public" && <RoomFacePile room={room} />}
{cannotJoin ? (
<div className="mx_RoomPreviewCard_notice">
<InfoSolidIcon />
{_t("room|join_failed_needs_invite", { roomName: room.name })}
</div>
) : null}
@@ -18,7 +18,6 @@ import React, {
type ReactNode,
useEffect,
} from "react";
import classNames from "classnames";
import {
RoomType,
HistoryVisibility,
@@ -28,6 +27,7 @@ import {
type ICreateRoomOpts,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { LockSolidIcon, PublicIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import ContextMenu, { ChevronFace } from "../../structures/ContextMenu";
@@ -47,6 +47,7 @@ import { Filter } from "../dialogs/spotlight/Filter";
import { type OpenSpotlightPayload } from "../../../dispatcher/payloads/OpenSpotlightPayload.ts";
import { useSettingValue } from "../../../hooks/useSettings.ts";
import { UIFeature } from "../../../settings/UIFeature.ts";
import SpacePillButton from "../../structures/SpacePillButton.tsx";
export const createSpace = async (
client: MatrixClient,
@@ -86,20 +87,6 @@ export const createSpace = async (
});
};
const SpaceCreateMenuType: React.FC<{
title: string;
description: string;
className: string;
onClick(): void;
}> = ({ title, description, className, onClick }) => {
return (
<AccessibleButton className={classNames("mx_SpaceCreateMenuType", className)} onClick={onClick}>
{title}
<div>{description}</div>
</AccessibleButton>
);
};
const spaceNameValidator = withValidation({
rules: [
{
@@ -285,16 +272,16 @@ const SpaceCreateMenu: React.FC<{
<h2>{_t("create_space|label")}</h2>
<p>{_t("create_space|explainer")}</p>
<SpaceCreateMenuType
<SpacePillButton
icon={<PublicIcon />}
title={_t("common|public")}
description={_t("create_space|public_description")}
className="mx_SpaceCreateMenuType_public"
onClick={() => setVisibility(Visibility.Public)}
/>
<SpaceCreateMenuType
<SpacePillButton
icon={<LockSolidIcon />}
title={_t("common|private")}
description={_t("create_space|private_description")}
className="mx_SpaceCreateMenuType_private"
onClick={() => setVisibility(Visibility.Private)}
/>
+19 -6
View File
@@ -22,6 +22,15 @@ import React, {
import { DragDropContext, Draggable, Droppable, type DroppableProvidedProps } from "react-beautiful-dnd";
import classNames from "classnames";
import { type Room } from "matrix-js-sdk/src/matrix";
import {
FavouriteSolidIcon,
HomeSolidIcon,
RoomIcon,
VideoCallSolidIcon,
UserProfileSolidIcon,
PlusIcon,
ChevronRightIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import { useContextMenu } from "../../structures/ContextMenu";
@@ -114,6 +123,7 @@ export const HomeButtonContextMenu: React.FC<ComponentProps<typeof SpaceContextM
interface IMetaSpaceButtonProps extends ComponentProps<typeof SpaceButton> {
selected: boolean;
isPanelCollapsed: boolean;
icon: JSX.Element;
}
type MetaSpaceButtonProps = Pick<IMetaSpaceButtonProps, "selected" | "isPanelCollapsed">;
@@ -152,7 +162,6 @@ const HomeButton: React.FC<MetaSpaceButtonProps> = ({ selected, isPanelCollapsed
return (
<MetaSpaceButton
spaceKey={MetaSpace.Home}
className="mx_SpaceButton_home"
selected={selected}
isPanelCollapsed={isPanelCollapsed}
label={getMetaSpaceName(MetaSpace.Home, allRoomsInHome)}
@@ -160,6 +169,7 @@ const HomeButton: React.FC<MetaSpaceButtonProps> = ({ selected, isPanelCollapsed
ContextMenuComponent={HomeButtonContextMenu}
contextMenuTooltip={_t("common|options")}
size="32px"
icon={<HomeSolidIcon />}
/>
);
};
@@ -168,12 +178,12 @@ const FavouritesButton: React.FC<MetaSpaceButtonProps> = ({ selected, isPanelCol
return (
<MetaSpaceButton
spaceKey={MetaSpace.Favourites}
className="mx_SpaceButton_favourites"
selected={selected}
isPanelCollapsed={isPanelCollapsed}
label={getMetaSpaceName(MetaSpace.Favourites)}
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.Favourites)}
size="32px"
icon={<FavouriteSolidIcon />}
/>
);
};
@@ -182,12 +192,12 @@ const PeopleButton: React.FC<MetaSpaceButtonProps> = ({ selected, isPanelCollaps
return (
<MetaSpaceButton
spaceKey={MetaSpace.People}
className="mx_SpaceButton_people"
selected={selected}
isPanelCollapsed={isPanelCollapsed}
label={getMetaSpaceName(MetaSpace.People)}
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.People)}
size="32px"
icon={<UserProfileSolidIcon />}
/>
);
};
@@ -196,12 +206,12 @@ const OrphansButton: React.FC<MetaSpaceButtonProps> = ({ selected, isPanelCollap
return (
<MetaSpaceButton
spaceKey={MetaSpace.Orphans}
className="mx_SpaceButton_orphans"
selected={selected}
isPanelCollapsed={isPanelCollapsed}
label={getMetaSpaceName(MetaSpace.Orphans)}
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.Orphans)}
size="32px"
icon={<RoomIcon />}
/>
);
};
@@ -210,12 +220,12 @@ const VideoRoomsButton: React.FC<MetaSpaceButtonProps> = ({ selected, isPanelCol
return (
<MetaSpaceButton
spaceKey={MetaSpace.VideoRooms}
className="mx_SpaceButton_videoRooms"
selected={selected}
isPanelCollapsed={isPanelCollapsed}
label={getMetaSpaceName(MetaSpace.VideoRooms)}
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.VideoRooms)}
size="32px"
icon={<VideoCallSolidIcon />}
/>
);
};
@@ -262,6 +272,7 @@ const CreateSpaceButton: React.FC<Pick<IInnerSpacePanelProps, "isPanelCollapsed"
isNarrow={isPanelCollapsed}
innerRef={handle}
size="32px"
icon={<PlusIcon />}
/>
{contextMenu}
@@ -449,7 +460,9 @@ const SpacePanel: React.FC = () => {
className="mx_SpacePanel_Tooltip_KeyboardShortcut"
/>
}
/>
>
<ChevronRightIcon />
</AccessibleButton>
</UserMenu>
<Droppable droppableId="top-level-spaces">
{(provided, snapshot) => (
@@ -9,15 +9,17 @@ Please see LICENSE files in the repository root for full details.
import React, { useState } from "react";
import { type Room } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
import { LinkIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
import { copyPlaintext } from "../../../utils/strings";
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
import { showRoomInviteDialog } from "../../../RoomInvite";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../settings/UIFeature";
import { Icon as InviteIcon } from "../../../../res/img/element-icons/room/invite.svg";
import SpacePillButton from "../../structures/SpacePillButton.tsx";
interface IProps {
space: Room;
@@ -29,8 +31,10 @@ const SpacePublicShare: React.FC<IProps> = ({ space, onFinished }) => {
return (
<div className="mx_SpacePublicShare">
<AccessibleButton
className="mx_SpacePublicShare_shareButton"
<SpacePillButton
icon={<LinkIcon />}
title={_t("space|invite_link")}
description={copiedText}
onClick={async (): Promise<void> => {
const permalinkCreator = new RoomPermalinkCreator(space);
permalinkCreator.load();
@@ -43,22 +47,18 @@ const SpacePublicShare: React.FC<IProps> = ({ space, onFinished }) => {
setCopiedText(_t("action|click_to_copy"));
}
}}
>
{_t("space|invite_link")}
<div>{copiedText}</div>
</AccessibleButton>
/>
{space.canInvite(MatrixClientPeg.safeGet().getSafeUserId()) &&
shouldShowComponent(UIComponent.InviteUsers) ? (
<AccessibleButton
className="mx_SpacePublicShare_inviteButton"
<SpacePillButton
icon={<InviteIcon />}
title={_t("space|invite")}
description={_t("space|invite_description")}
onClick={() => {
if (onFinished) onFinished();
showRoomInviteDialog(space.roomId);
}}
>
{_t("space|invite")}
<div>{_t("space|invite_description")}</div>
</AccessibleButton>
/>
) : null}
</div>
);
+11 -2
View File
@@ -20,6 +20,11 @@ import classNames from "classnames";
import { type Room, RoomEvent } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { type DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
import {
ChevronDownIcon,
ChevronRightIcon,
OverflowHorizontalIcon,
} from "@vector-im/compound-design-tokens/assets/web/icons";
import RoomAvatar from "../avatars/RoomAvatar";
import SpaceStore from "../../../stores/spaces/SpaceStore";
@@ -169,7 +174,9 @@ export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({
onClick={openMenu}
title={contextMenuTooltip}
isExpanded={menuDisplayed}
/>
>
<OverflowHorizontalIcon />
</ContextMenuTooltipButton>
)}
{contextMenu}
@@ -349,7 +356,9 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
onClick={this.toggleCollapse}
tabIndex={-1}
aria-label={collapsed ? _t("action|expand") : _t("action|collapse")}
/>
>
{collapsed ? <ChevronRightIcon /> : <ChevronDownIcon />}
</AccessibleButton>
) : null;
// eslint-disable-next-line @typescript-eslint/no-unused-vars