Merge branch 'hotfix/v2.5.1' into feature/langs-updates_0524

This commit is contained in:
Alexey Safronov 2024-05-13 19:04:49 +04:00
commit 1f9bc33150
33 changed files with 305 additions and 77 deletions

View File

@ -114,6 +114,7 @@ export default function withQuickButtons(WrappedComponent) {
isPublicRoom,
isPersonalRoom,
isArchiveFolder,
currentDeviceType,
} = this.props;
const quickButtonsComponent = (
@ -134,6 +135,7 @@ export default function withQuickButtons(WrappedComponent) {
folderCategory={folderCategory}
onCopyPrimaryLink={this.onCopyPrimaryLink}
isArchiveFolder={isArchiveFolder}
currentDeviceType={currentDeviceType}
/>
);
@ -177,6 +179,7 @@ export default function withQuickButtons(WrappedComponent) {
return {
theme: settingsStore.theme,
currentDeviceType: settingsStore.currentDeviceType,
isAdmin: authStore.isAdmin,
lockFileAction,
setFavoriteAction,

View File

@ -39,7 +39,8 @@ const Banner = ({
setSubmitToGalleryDialogVisible,
setClosedCampaigns,
getBanner,
campaignImage,
campaignBackground,
campaignIcon,
campaignTranslate,
campaignConfig,
currentCampaign,
@ -73,12 +74,13 @@ const Banner = ({
return (
<StyledWrapper>
{campaignImage &&
{campaignBackground &&
campaignTranslate &&
campaignConfig &&
currentCampaign && (
<CampaignsBanner
campaignImage={campaignImage}
campaignBackground={campaignBackground}
campaignIcon={campaignIcon}
campaignTranslate={campaignTranslate}
campaignConfig={campaignConfig}
onAction={onAction}
@ -94,7 +96,8 @@ export default inject(({ dialogsStore, campaignsStore }) => {
const {
setClosedCampaigns,
getBanner,
campaignImage,
campaignBackground,
campaignIcon,
campaignTranslate,
campaignConfig,
currentCampaign,
@ -104,7 +107,8 @@ export default inject(({ dialogsStore, campaignsStore }) => {
setSubmitToGalleryDialogVisible,
setClosedCampaigns,
getBanner,
campaignImage,
campaignBackground,
campaignIcon,
campaignTranslate,
campaignConfig,
currentCampaign,

View File

@ -30,12 +30,14 @@ import LinkReactSvgUrl from "PUBLIC_DIR/images/link.react.svg?url";
import LockedReactSvgUrl from "PUBLIC_DIR/images/locked.react.svg?url";
import FileActionsFavoriteReactSvgUrl from "PUBLIC_DIR/images/file.actions.favorite.react.svg?url";
import FavoriteReactSvgUrl from "PUBLIC_DIR/images/favorite.react.svg?url";
import LockedReact12SvgUrl from "PUBLIC_DIR/images/icons/12/lock.react.svg?url";
import React from "react";
import React, { useMemo } from "react";
import styled from "styled-components";
import { isTablet, isMobile, commonIconsStyles } from "@docspace/shared/utils";
import {
DeviceType,
FileStatus,
RoomsType,
ShareAccessRights,
@ -60,16 +62,26 @@ const QuickButtons = (props) => {
onClickShare,
isPersonalRoom,
isArchiveFolder,
currentDeviceType,
} = props;
const isMobile = currentDeviceType === DeviceType.mobile;
const { id, locked, shared, fileStatus, title, fileExst } = item;
const isFavorite =
(fileStatus & FileStatus.IsFavorite) === FileStatus.IsFavorite;
const isTile = viewAs === "tile";
const isRow = viewAs == "row";
const iconLock = locked ? FileActionsLockedReactSvgUrl : LockedReactSvgUrl;
const iconLock = useMemo(() => {
if (isMobile) {
return LockedReact12SvgUrl;
}
return locked ? FileActionsLockedReactSvgUrl : LockedReactSvgUrl;
}, [locked, isMobile]);
const colorLock = locked
? theme.filesQuickButtons.sharedColor
@ -90,13 +102,17 @@ const QuickButtons = (props) => {
const tabletViewQuickButton = isTablet();
const sizeQuickButton = isTile || tabletViewQuickButton ? "medium" : "small";
const displayBadges = viewAs === "table" || isTile || tabletViewQuickButton;
const displayBadges =
viewAs === "table" ||
(isRow && locked && isMobile) ||
isTile ||
tabletViewQuickButton;
const setFavorite = () => onClickFavorite(isFavorite);
const isAvailableLockFile =
!folderCategory && fileExst && displayBadges && item.security.Lock;
const isAvailableDownloadFile =
isPublicRoom && item.security.Download && viewAs === "tile";

View File

@ -93,6 +93,7 @@ const TagDropdown = ({
heightTablet={32}
key={i}
label={tag}
onMouseDown={preventDefault}
onClick={() => addFetchedTag(tag)}
/>
));

View File

@ -71,6 +71,7 @@ const Members = ({
setExternalLink,
withPublicRoomBlock,
fetchMembers,
fetchMoreMembers,
membersIsLoading,
searchValue,
searchResultIsLoading,
@ -96,19 +97,7 @@ const Members = ({
}, [infoPanelSelection, searchValue]);
const loadNextPage = async () => {
const roomId = infoPanelSelection.id;
const fetchedMembers = await fetchMembers(t, false, withoutTitlesAndLinks);
const { users, administrators, expected, groups } = fetchedMembers;
const newMembers = {
roomId: roomId,
administrators: [...infoPanelMembers.administrators, ...administrators],
users: [...infoPanelMembers.users, ...users],
expected: [...infoPanelMembers.expected, ...expected],
groups: [...infoPanelMembers.groups, ...groups],
};
setInfoPanelMembers(newMembers);
await fetchMoreMembers(t, withoutTitlesAndLinks);
};
if (membersIsLoading) return <InfoPanelViewLoader view="members" />;
@ -301,6 +290,7 @@ export default inject(
infoPanelMembers,
setInfoPanelMembers,
fetchMembers,
fetchMoreMembers,
membersIsLoading,
withPublicRoomBlock,
searchValue,
@ -344,6 +334,7 @@ export default inject(
setExternalLink,
withPublicRoomBlock,
fetchMembers,
fetchMoreMembers,
membersIsLoading,
searchValue,
searchResultIsLoading,

View File

@ -89,7 +89,7 @@ const MembersList = (props) => {
});
const listOfTitles = list
.filter((x) => x.props.isTitle)
.filter((x) => x.props.user?.isTitle)
.map((item) => {
return {
displayName: item.props.user.displayName,

View File

@ -35,7 +35,7 @@ import { useTranslation } from "react-i18next";
import { Text } from "@docspace/shared/components/text";
import { StyledSeveralItemsContainer } from "../../styles/severalItems";
const SeveralItems = ({ isAccounts, theme, selectedItems }) => {
const SeveralItems = ({ isPeople, theme, selectedItems }) => {
const { t } = useTranslation("InfoPanel");
const emptyScreenAlt = theme.isBase
@ -46,15 +46,15 @@ const SeveralItems = ({ isAccounts, theme, selectedItems }) => {
? EmptyScreenPersonSvgUrl
: EmptyScreenPersonSvgDarkUrl;
const imgSrc = isAccounts ? emptyScreenPerson : emptyScreenAlt;
const imgSrc = isPeople ? emptyScreenPerson : emptyScreenAlt;
const itemsText = isAccounts
const itemsText = isPeople
? t("InfoPanel:SelectedUsers")
: t("InfoPanel:ItemsSelected");
return (
<StyledSeveralItemsContainer
isAccounts={isAccounts}
isPeople={isPeople}
className="no-thumbnail-img-wrapper"
>
<img src={imgSrc} />

View File

@ -47,7 +47,7 @@ const StyledRowContent = styled(RowContent)`
.badges {
flex-direction: row-reverse;
margin-top: 10px;
margin-inline-end: 12px;
.paid-badge {

View File

@ -46,7 +46,7 @@ const StyledRowContent = styled(RowContent)`
.badges {
flex-direction: row-reverse;
margin-top: 9px;
margin-inline-end: 12px;
.paid-badge {

View File

@ -175,13 +175,6 @@ const StyledSimpleFilesRow = styled(Row)`
}
}
.lock-file {
cursor: ${(props) => (props.withAccess ? "pointer" : "default")};
svg {
height: 12px;
}
}
.tablet-row-copy-link {
display: none;
}
@ -241,6 +234,7 @@ const StyledSimpleFilesRow = styled(Row)`
}
.lock-file {
cursor: ${(props) => (props.withAccess ? "pointer" : "default")};
svg {
height: 16px;
}
@ -292,20 +286,26 @@ const StyledSimpleFilesRow = styled(Row)`
}
@media ${mobile} {
.lock-file {
svg {
height: 12px;
}
}
.badges {
gap: 8px;
}
.badges__quickButtons:not(:empty) {
/* .badges__quickButtons:not(:empty) {
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
margin-right: 8px;
`
: css`
margin-left: 8px;
`}
}
props.theme.interfaceDirection === "rtl"
? css`
margin-right: 8px;
`
: css`
margin-left: 8px;
`}
} */
.room__badges:empty,
.file__badges:empty,
.folder__badges:empty,

View File

@ -714,6 +714,7 @@ class Tile extends React.PureComponent {
isThirdParty: true,
icon: item.thirdPartyIcon,
label: item.providerKey,
providerType: item.providerType,
onClick: () =>
selectOption({
option: "typeProvider",

View File

@ -55,6 +55,7 @@ const Sdk = ({
getRoomsIcon,
fetchExternalLinks,
getFilePrimaryLink,
getFilesSettings,
}) => {
const [isDataReady, setIsDataReady] = useState(false);
@ -96,6 +97,10 @@ const Sdk = ({
}
}, [callCommandLoad, isDataReady]);
useEffect(() => {
getFilesSettings();
}, []);
const { mode } = useParams();
const selectorType = new URLSearchParams(window.location.search).get(
"selectorType",
@ -291,7 +296,7 @@ export default inject(
settingsStore;
const { loadCurrentUser, user } = userStore;
const { updateProfileCulture } = peopleStore.targetUserStore;
const { getIcon, getRoomsIcon } = filesSettingsStore;
const { getIcon, getRoomsIcon, getFilesSettings } = filesSettingsStore;
const { fetchExternalLinks } = publicRoomStore;
const { getFilePrimaryLink } = filesStore;
@ -310,6 +315,7 @@ export default inject(
user,
fetchExternalLinks,
getFilePrimaryLink,
getFilesSettings,
};
},
)(withTranslation(["JavascriptSdk", "Common"])(observer(Sdk)));

View File

@ -190,6 +190,10 @@ const VersionRow = (props) => {
// );
// };
const onContextMenu = (event) => {
if (showEditPanel) event.stopPropagation();
};
const contextOptions = [
{
key: "open",
@ -241,7 +245,7 @@ const VersionRow = (props) => {
isEditing={isEditing}
contextTitle={t("Common:Actions")}
>
<div className={`version-row_${index}`}>
<div className={`version-row_${index}`} onContextMenu={onContextMenu}>
<Box displayProp="flex" className="row-header">
<VersionBadge
theme={theme}

View File

@ -45,7 +45,9 @@ class CampaignsStore {
userStore: UserStore = {} as UserStore;
campaignImage: string | null = null;
campaignBackground: string | null = null;
campaignIcon: string | null = null;
campaignTranslate: string | null = null;
@ -99,6 +101,7 @@ class CampaignsStore {
}
const image = await getImage(currentCampaign, standalone);
const icon = await getImage(currentCampaign, standalone, true);
const translate = await getTranslation(
currentCampaign,
language,
@ -113,7 +116,8 @@ class CampaignsStore {
runInAction(() => {
this.currentCampaign = currentCampaign;
this.campaignImage = image;
this.campaignBackground = image;
this.campaignIcon = icon;
this.campaignTranslate = translate;
this.campaignConfig = config;
});

View File

@ -1211,6 +1211,11 @@ class FilesActionStore {
}
// this.updateCurrentFolder(null, null, null, operationId);
this.dialogsStore.setIsFolderActions(false);
return setTimeout(
() => clearSecondaryProgressData(operationId),
TIMEOUT,
);
})
.then(() => {
@ -1264,6 +1269,11 @@ class FilesActionStore {
await this.uploadDataStore.loopFilesOperations(data, pbData);
// this.updateCurrentFolder(null, [items], null, operationId);
this.dialogsStore.setIsFolderActions(false);
return setTimeout(
() => clearSecondaryProgressData(operationId),
TIMEOUT,
);
})
.then(() => {

View File

@ -133,8 +133,10 @@ class InfoPanelStore {
};
setSearchValue = (value) => {
this.setSearchResultIsLoading(true);
this.searchValue = value;
if (value !== this.searchValue) {
this.setSearchResultIsLoading(true);
this.searchValue = value;
}
};
resetSearch = () => {
@ -651,6 +653,38 @@ class InfoPanelStore {
};
};
fetchMoreMembers = async (t, withoutTitles) => {
const roomId = this.infoPanelSelection.id;
const oldMembers = this.infoPanelMembers;
const data = await this.filesStore.getRoomMembers(roomId, false);
const newMembers = this.convertMembers(t, data, false, true);
const mergedMembers = {
roomId: roomId,
administrators: [
...oldMembers.administrators,
...newMembers.administrators,
],
users: [...oldMembers.users, ...newMembers.users],
expected: [...oldMembers.expected, ...newMembers.expectedMembers],
groups: [...oldMembers.groups, ...newMembers.groups],
};
if (!withoutTitles) {
this.addMembersTitle(
t,
mergedMembers.administrators,
mergedMembers.users,
mergedMembers.expected,
mergedMembers.groups,
);
}
this.setInfoPanelMembers(mergedMembers);
};
addInfoPanelMembers = (t, members) => {
const convertedMembers = this.convertMembers(t, members);

View File

@ -63,6 +63,8 @@ const Root = ({
fileId,
hash,
}: TResponse) => {
const editorRef = React.useRef<null | HTMLElement>(null);
const documentserverUrl = config?.editorUrl ?? error?.editorUrl;
const fileInfo = config?.file;
@ -141,9 +143,19 @@ const Root = ({
isSharingDialogVisible ||
isVisibleSelectFolderDialog ||
selectFileDialogVisible
)
) {
calculateAsideHeight();
const activeElement = document.activeElement as HTMLElement | null;
if (activeElement && activeElement.tagName === "IFRAME") {
editorRef.current = activeElement;
activeElement.blur();
}
} else if (editorRef.current) {
editorRef.current.focus();
}
if (isSharingDialogVisible) {
setTimeout(calculateAsideHeight, 10);
}

View File

@ -170,8 +170,8 @@ const Login: React.FC<ILoginProps> = ({
};
const onSocialButtonClick = useCallback(
(e: HTMLElementEvent<HTMLButtonElement | HTMLElement>) => {
const { target } = e;
(e: React.MouseEvent<Element, MouseEvent>) => {
const target = e.target as HTMLElement;
let targetElement = target;
if (

View File

@ -55,7 +55,7 @@ const Template = (args: CampaignsBannerProps) => (
export const Default: Story = {
render: (args) => <Template {...args} />,
args: {
campaignImage: "", // TODO: add url on image
campaignBackground: "", // TODO: add url on image
campaignTranslate: translates,
campaignConfig: config,
},

View File

@ -26,7 +26,7 @@
import styled, { css } from "styled-components";
import { Base } from "../../themes";
import { desktop } from "../../utils/device";
import { tablet, mobile } from "../../utils/device";
const BannerWrapper = styled.div<{
background?: string;
@ -58,6 +58,11 @@ const BannerWrapper = styled.div<{
`}
}
@media ${mobile} {
min-height: 132px;
max-height: 132px;
}
.close-icon {
position: absolute;
${(props) =>
@ -85,11 +90,23 @@ const BannerContent = styled.div`
flex-direction: column;
gap: 8px;
@media ${desktop} {
.header {
max-width: 167px;
}
@media ${tablet} {
.header {
max-width: 167px;
max-width: 180px;
}
}
@media ${mobile} {
.header {
max-width: 75%;
}
max-width: 75%;
}
`;
const BannerButton = styled.button<{
@ -108,4 +125,32 @@ const BannerButton = styled.button<{
color: ${(props) => props.buttonTextColor};
`;
export { BannerWrapper, BannerContent, BannerButton };
const BannerIcon = styled.div`
width: 100px;
height: 80px;
z-index: -1;
position: absolute;
bottom: 1px;
${(props) =>
props.theme.interfaceDirection === "rtl"
? css`
left: 1px;
transform: scaleX(-1);
`
: css`
right: 1px;
`}
@media ${mobile} {
width: 140px;
height: 112px;
svg {
width: 140px;
height: 112px;
}
}
`;
export { BannerWrapper, BannerContent, BannerButton, BannerIcon };

View File

@ -36,7 +36,7 @@ describe("<CampaignsBanner />", () => {
it("renders without error", () => {
render(
<CampaignsBanner
campaignImage="" // TODO: add url on image
campaignBackground="" // TODO: add url on image
campaignTranslate={translates}
campaignConfig={config}
onClose={() => null}

View File

@ -27,6 +27,7 @@
import CrossReactSvg from "PUBLIC_DIR/images/cross.react.svg?url";
import React from "react";
import { ReactSVG } from "react-svg";
import { Text as TextComponent } from "../text";
import { Link as LinkComponent } from "../link";
@ -36,6 +37,7 @@ import {
BannerWrapper,
BannerContent,
BannerButton,
BannerIcon,
} from "./CampaignsBanner.styled";
import { CampaignsBannerProps } from "./CampaignsBanner.types";
@ -43,7 +45,8 @@ import useFitText from "./useFitText";
const CampaignsBanner = (props: CampaignsBannerProps) => {
const {
campaignImage,
campaignBackground,
campaignIcon,
campaignTranslate,
campaignConfig,
onAction,
@ -57,16 +60,19 @@ const CampaignsBanner = (props: CampaignsBannerProps) => {
const hasText = !!Text;
const isButton = action?.isButton;
const { fontSize, ref } = useFitText(campaignImage, body?.fontSize);
const { fontSize, ref, wrapperRef } = useFitText(
campaignBackground,
body?.fontSize,
);
return (
<BannerWrapper
ref={ref}
ref={wrapperRef}
data-testid="campaigns-banner"
background={campaignImage}
background={campaignBackground}
borderColor={borderColor}
>
<BannerContent>
<BannerContent ref={ref}>
{hasTitle && (
<TextComponent
className="header"
@ -124,6 +130,9 @@ const CampaignsBanner = (props: CampaignsBannerProps) => {
iconName={CrossReactSvg}
onClick={onClose}
/>
<BannerIcon>
<ReactSVG src={campaignIcon} />
</BannerIcon>
</BannerWrapper>
);
};

View File

@ -32,7 +32,9 @@ export interface CampaignsBannerProps {
/** Accepts css style */
style?: React.CSSProperties;
/** Background */
campaignImage: string;
campaignBackground: string;
/** Icon */
campaignIcon: string;
/** Translations */
campaignTranslate: ITranslate;
/** Config */

View File

@ -27,28 +27,31 @@
import { useRef, useState, useEffect } from "react";
const useFitText = (
campaignImage: string,
campaignBackground: string,
currentFontSize: string = "13px",
) => {
const ref: React.RefObject<HTMLDivElement> = useRef(null);
const wrapperRef: React.RefObject<HTMLDivElement> = useRef(null);
const [fontSize, setFontSize] = useState(parseInt(currentFontSize, 10));
useEffect(() => {
setFontSize(parseInt(currentFontSize, 10));
}, [campaignImage, currentFontSize]);
}, [campaignBackground, currentFontSize]);
useEffect(() => {
const isOverflow =
!!ref.current && ref.current.scrollHeight > ref.current.offsetHeight;
!!ref.current &&
!!wrapperRef.current &&
ref.current.scrollHeight > wrapperRef.current.offsetHeight;
if (isOverflow) {
const newFontSize = fontSize - 1;
const newFontSize = fontSize - 2;
setFontSize(newFontSize);
}
}, [currentFontSize, fontSize, ref?.current?.scrollHeight]);
}, [fontSize]);
return { fontSize: `${fontSize}px`, ref };
return { fontSize: `${fontSize}px`, ref, wrapperRef };
};
export default useFitText;

View File

@ -210,7 +210,8 @@ const SortButton = ({
advancedOptionsCount={advancedOptionsCount}
onSelect={() => {}}
withBlur={false}
withBackdrop={false}
withBackdrop
onBackdropClick={toggleCombobox}
>
<IconButton iconName={SortReactSvgUrl} size={16} />
</ComboBox>

View File

@ -909,6 +909,20 @@ export const ImageViewer = ({
});
}, [src, imageId, version, isTiff, loadImage, changeSource, thumbnailSrc]);
useEffect(() => {
const onWheelEvent = (event: WheelEvent) => {
if (event.ctrlKey) event.preventDefault();
};
window.addEventListener("wheel", onWheelEvent, {
passive: false,
});
return () => {
window.removeEventListener("wheel", onWheelEvent);
};
}, []);
return (
<>
{isMobile && !backgroundBlack && mobileDetails}

View File

@ -33,7 +33,7 @@ const loadingAnimation = keyframes`
}
100% {
transform: translateX(300%);
transform: translateX(400%);
}
`;

View File

@ -37,11 +37,18 @@ export const getCampaignsLs = (standalone: boolean) => {
export const getImage = async (
campaign: string,
standalone: boolean,
isIcon: boolean = false,
): Promise<string> => {
if (standalone) {
if (isIcon) {
return `/static/campaigns/images/campaign.${campaign.toLowerCase()}.icon.svg`;
}
return `/static/campaigns/images/campaign.${campaign.toLowerCase()}.svg`;
}
const imageUrl = await window.firebaseHelper.getCampaignsImages(campaign);
const imageUrl = await window.firebaseHelper.getCampaignsImages(
campaign,
isIcon,
);
return imageUrl;
};

View File

@ -633,7 +633,7 @@ export const getPowerFromBytes = (bytes: number, maxPower = 6) => {
};
export const getSizeFromBytes = (bytes: number, power: number) => {
return parseFloat((bytes / 1024 ** power).toFixed(2));
return Math.floor(bytes / 1024 ** power);
};
export const getConvertedSize = (t: (key: string) => string, bytes: number) => {

View File

@ -171,8 +171,11 @@ class FirebaseHelper {
return Promise.resolve(campaigns);
}
async getCampaignsImages(campaign: string) {
async getCampaignsImages(campaign: string, isIcon: boolean) {
const domain = this.config?.authDomain;
if (isIcon) {
return `https://${domain}/images/campaign.${campaign.toLowerCase()}.icon.svg`;
}
return `https://${domain}/images/campaign.${campaign.toLowerCase()}.svg`;
}

View File

@ -52,6 +52,7 @@ import RoReactSvgUrl from "PUBLIC_DIR/images/flags/ro.react.svg?url";
import RuReactSvgUrl from "PUBLIC_DIR/images/flags/ru.react.svg?url";
import SkReactSvgUrl from "PUBLIC_DIR/images/flags/sk.react.svg?url";
import SlReactSvgUrl from "PUBLIC_DIR/images/flags/sl.react.svg?url";
import SiReactSvgUrl from "PUBLIC_DIR/images/flags/si.react.svg?url";
import SrLatnRSReactSvgUrl from "PUBLIC_DIR/images/flags/sr-Latn-RS.react.svg?url";
import TrReactSvgUrl from "PUBLIC_DIR/images/flags/tr.react.svg?url";
import UkUAReactSvgUrl from "PUBLIC_DIR/images/flags/uk-UA.react.svg?url";
@ -87,6 +88,7 @@ export const flagsIcons = new Map([
["ru.react.svg", RuReactSvgUrl],
["sk.react.svg", SkReactSvgUrl],
["sl.react.svg", SlReactSvgUrl],
["si.react.svg", SiReactSvgUrl],
["sr-Latn-RS.react.svg", SrLatnRSReactSvgUrl],
["tr.react.svg", TrReactSvgUrl],
["uk-UA.react.svg", UkUAReactSvgUrl],

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 0C4.34315 0 3 1.34315 3 3V4C1.89543 4 1 4.89543 1 6V10C1 11.1046 1.89543 12 3 12H9C10.1046 12 11 11.1046 11 10V6C11 4.89543 10.1046 4 9 4V3C9 1.34315 7.65685 0 6 0ZM7 4V3C7 2.44772 6.55228 2 6 2C5.44772 2 5 2.44772 5 3V4H7ZM7 8C7 8.55228 6.55228 9 6 9C5.44772 9 5 8.55228 5 8C5 7.44772 5.44772 7 6 7C6.55228 7 7 7.44772 7 8Z" fill="#657077"/>
</svg>

After

Width:  |  Height:  |  Size: 498 B