@meshsdk/react 1.5.30 → 1.6.0-alpha.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js ADDED
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ extends: ["@meshsdk/eslint-config/react.js"],
3
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshsdk/react",
3
- "version": "1.5.30",
3
+ "version": "1.6.0-alpha.20",
4
4
  "sideEffects": [
5
5
  "**/*.css"
6
6
  ],
@@ -8,9 +8,6 @@
8
8
  "./styles.css": "./dist/index.css",
9
9
  ".": "./src/index.ts"
10
10
  },
11
- "files": [
12
- "dist/**"
13
- ],
14
11
  "license": "MIT",
15
12
  "scripts": {
16
13
  "build:mesh": "tailwindcss -i ./src/styles.css -o ./dist/index.css",
@@ -35,8 +32,5 @@
35
32
  "tailwindcss": "^3.4.1",
36
33
  "typescript": "latest"
37
34
  },
38
- "prettier": "@meshsdk/prettier-config",
39
- "publishConfig": {
40
- "access": "public"
41
- }
35
+ "prettier": "@meshsdk/prettier-config"
42
36
  }
@@ -0,0 +1,9 @@
1
+ // If you want to use other PostCSS plugins, see the following:
2
+ // https://tailwindcss.com/docs/using-with-preprocessors
3
+
4
+ module.exports = {
5
+ plugins: {
6
+ tailwindcss: {},
7
+ autoprefixer: {},
8
+ },
9
+ };
@@ -0,0 +1,10 @@
1
+ export const CheckMark = () => (
2
+ <svg
3
+ className="ui-m-2 ui-h-6"
4
+ fill="currentColor"
5
+ viewBox="0 0 512 512"
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ >
8
+ <path d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" />
9
+ </svg>
10
+ );
@@ -0,0 +1,17 @@
1
+ export const ChevronDown = () => (
2
+ <svg
3
+ className="ui-m-2 ui-h-6"
4
+ fill="none"
5
+ aria-hidden="true"
6
+ viewBox="0 0 24 24"
7
+ stroke="currentColor"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ >
10
+ <path
11
+ strokeLinecap="round"
12
+ strokeLinejoin="round"
13
+ strokeWidth="2"
14
+ d="M19 9l-7 7-7-7"
15
+ />
16
+ </svg>
17
+ );
@@ -0,0 +1,106 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ import { Wallet } from "@meshsdk/common";
4
+ import { BrowserWallet } from "@meshsdk/core";
5
+
6
+ import Button from "../common/button";
7
+ import { useWallet } from "../hooks";
8
+ import { MenuItem } from "./menu-item";
9
+ import { WalletBalance } from "./wallet-balance";
10
+
11
+ interface ButtonProps {
12
+ label?: string;
13
+ onConnected?: Function;
14
+ isDark?: boolean;
15
+ }
16
+
17
+ export const CardanoWallet = ({
18
+ label = "Connect Wallet",
19
+ onConnected = undefined,
20
+ isDark = false,
21
+ }: ButtonProps) => {
22
+ const [isDarkMode, setIsDarkMode] = useState(false);
23
+ const [hideMenuList, setHideMenuList] = useState(true);
24
+ const [wallets, setWallets] = useState<Wallet[]>([]);
25
+
26
+ const { connect, connecting, connected, disconnect, name } = useWallet();
27
+
28
+ useEffect(() => {
29
+ if (connected && onConnected) {
30
+ onConnected();
31
+ }
32
+ }, [connected]);
33
+
34
+ useEffect(() => {
35
+ setWallets(BrowserWallet.getInstalledWallets());
36
+ }, []);
37
+
38
+ useEffect(() => {
39
+ setIsDarkMode(isDark);
40
+ }, [isDark]);
41
+
42
+ return (
43
+ <div
44
+ className="w-fit"
45
+ onMouseEnter={() => setHideMenuList(false)}
46
+ onMouseLeave={() => setHideMenuList(true)}
47
+ >
48
+ <Button
49
+ isDarkMode={isDarkMode}
50
+ hideMenuList={hideMenuList}
51
+ setHideMenuList={setHideMenuList}
52
+ >
53
+ <WalletBalance
54
+ connected={connected}
55
+ connecting={connecting}
56
+ label={label}
57
+ wallet={wallets.find((wallet) => wallet.name === name)}
58
+ />
59
+ </Button>
60
+ <div
61
+ className={`ui-mr-menu-list ui-absolute ui-w-60 ui-rounded-b-md ui-border ui-text-center ui-shadow-sm ui-backdrop-blur ${hideMenuList && "ui-hidden"} ${isDarkMode ? `ui-bg-neutral-950 ui-text-neutral-50` : `ui-bg-neutral-50 ui-text-neutral-950`}`}
62
+ >
63
+ {!connected && wallets.length > 0 ? (
64
+ <>
65
+ {wallets.map((wallet, index) => (
66
+ <MenuItem
67
+ key={index}
68
+ icon={wallet.icon}
69
+ label={wallet.name}
70
+ action={() => {
71
+ connect(wallet.name);
72
+ setHideMenuList(!hideMenuList);
73
+ }}
74
+ active={name === wallet.name}
75
+ />
76
+ ))}
77
+ {/* <MenuItem
78
+ icon={
79
+ isDarkMode
80
+ ? `https://meshjs.dev/logo-mesh/white/logo-mesh-white-128x128.png`
81
+ : `https://meshjs.dev/logo-mesh/black/logo-mesh-black-128x128.png`
82
+ }
83
+ label={'Local'}
84
+ action={() => {
85
+ connectLocalWallet();
86
+ setHideMenuList(!hideMenuList);
87
+ }}
88
+ active={false}
89
+ /> */}
90
+ </>
91
+ ) : wallets.length === 0 ? (
92
+ <span>No Wallet Found</span>
93
+ ) : (
94
+ <>
95
+ <MenuItem
96
+ active={false}
97
+ label="disconnect"
98
+ action={disconnect}
99
+ icon={undefined}
100
+ />
101
+ </>
102
+ )}
103
+ </div>
104
+ </div>
105
+ );
106
+ };
@@ -0,0 +1,28 @@
1
+ export function MenuItem({
2
+ icon,
3
+ label,
4
+ action,
5
+ active,
6
+ }: {
7
+ icon?: string;
8
+ label: string;
9
+ action: () => void;
10
+ active: boolean;
11
+ }) {
12
+ return (
13
+ <div
14
+ className="ui-flex ui-cursor-pointer ui-items-center ui-px-4 ui-py-2 ui-opacity-80 hover:ui-opacity-100 ui-h-16"
15
+ onClick={action}
16
+ >
17
+ {icon && <img className="ui-pr-2 m-1 h-8" src={icon} />}
18
+ <span className="ui-mr-menu-item ui-text-xl ui-font-normal">
19
+ {label
20
+ .split(" ")
21
+ .map((word: string) => {
22
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
23
+ })
24
+ .join(" ")}
25
+ </span>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,36 @@
1
+ import { Wallet } from "@meshsdk/common";
2
+ import { useLovelace } from "@meshsdk/react";
3
+
4
+ import { ChevronDown } from "./chevron-down";
5
+
6
+ export const WalletBalance = ({
7
+ connected,
8
+ connecting,
9
+ label,
10
+ wallet,
11
+ }: {
12
+ connected: boolean;
13
+ connecting: boolean;
14
+ label: string;
15
+ wallet: Wallet | undefined;
16
+ }) => {
17
+ const lovelace = useLovelace();
18
+
19
+ return connected && lovelace && wallet?.icon ? (
20
+ <>
21
+ <img className="ui-m-2 ui-h-6" src={wallet.icon} />₳{" "}
22
+ {parseInt((parseInt(lovelace, 10) / 1_000_000).toString(), 10)}.
23
+ <span className="text-xs">{lovelace.substring(lovelace.length - 6)}</span>
24
+ </>
25
+ ) : connected && wallet?.icon ? (
26
+ <>
27
+ <img className="ui-m-2 ui-h-6" src={wallet.icon} />
28
+ </>
29
+ ) : connecting ? (
30
+ <>Connecting...</>
31
+ ) : (
32
+ <>
33
+ {label} <ChevronDown />
34
+ </>
35
+ );
36
+ };
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+
3
+ export default function Button({
4
+ children,
5
+ isDarkMode = false,
6
+ hideMenuList = false,
7
+ setHideMenuList,
8
+ }: {
9
+ children: React.ReactNode;
10
+ isDarkMode?: boolean;
11
+ hideMenuList?: boolean;
12
+ setHideMenuList?: (hideMenuList: boolean) => void;
13
+ }) {
14
+ return (
15
+ <button
16
+ className={`ui-mr-menu-list ui-flex ui-w-60 ui-items-center ui-justify-center ui-rounded-t-md ui-border ui-px-4 ui-py-2 ui-text-lg ui-font-normal ui-shadow-sm ${isDarkMode ? `ui-bg-neutral-950 ui-text-neutral-50` : `ui-bg-neutral-50 ui-text-neutral-950`}`}
17
+ onClick={() => setHideMenuList && setHideMenuList(!hideMenuList)}
18
+ >
19
+ {children}
20
+ </button>
21
+ );
22
+ }
@@ -0,0 +1,76 @@
1
+ import { createContext, useCallback, useState } from "react";
2
+
3
+ import { BrowserWallet } from "@meshsdk/wallet";
4
+
5
+ interface WalletContext {
6
+ hasConnectedWallet: boolean;
7
+ connectedWalletInstance: BrowserWallet;
8
+ connectedWalletName: string;
9
+ connectingWallet: boolean;
10
+ connectWallet?: (walletName: string) => Promise<void>;
11
+ disconnect?: () => void;
12
+ // setWallet?: () => void,
13
+ error?: unknown;
14
+ }
15
+
16
+ const INITIAL_STATE = {
17
+ walletName: "",
18
+ walletInstance: {} as BrowserWallet,
19
+ };
20
+
21
+ export const useWalletStore = () => {
22
+ const [error, setError] = useState<unknown>(undefined);
23
+
24
+ const [connectingWallet, setConnectingWallet] = useState<boolean>(false);
25
+
26
+ const [connectedWalletInstance, setConnectedWalletInstance] =
27
+ useState<BrowserWallet>(INITIAL_STATE.walletInstance);
28
+
29
+ const [connectedWalletName, setConnectedWalletName] = useState<string>(
30
+ INITIAL_STATE.walletName,
31
+ );
32
+
33
+ const connectWallet = useCallback(async (walletName: string) => {
34
+ setConnectingWallet(true);
35
+
36
+ try {
37
+ const walletInstance = await BrowserWallet.enable(walletName);
38
+ setConnectedWalletInstance(walletInstance);
39
+ setConnectedWalletName(walletName);
40
+ setError(undefined);
41
+ } catch (error) {
42
+ setError(error);
43
+ console.error(error);
44
+ }
45
+
46
+ setConnectingWallet(false);
47
+ }, []);
48
+
49
+ const disconnect = useCallback(() => {
50
+ setConnectedWalletName(INITIAL_STATE.walletName);
51
+ setConnectedWalletInstance(INITIAL_STATE.walletInstance);
52
+ }, []);
53
+
54
+ // const setWallet = useCallback((wallet: AppWallet) => {
55
+ // setConnectedWalletName(INITIAL_STATE.walletName);
56
+ // setConnectedWalletInstance(wallet);
57
+ // }, []);
58
+
59
+ return {
60
+ hasConnectedWallet: INITIAL_STATE.walletName !== connectedWalletName,
61
+ connectedWalletInstance,
62
+ connectedWalletName,
63
+ connectingWallet,
64
+ connectWallet,
65
+ disconnect,
66
+ // setWallet,
67
+ error,
68
+ };
69
+ };
70
+
71
+ export const WalletContext = createContext<WalletContext>({
72
+ hasConnectedWallet: false,
73
+ connectedWalletInstance: INITIAL_STATE.walletInstance,
74
+ connectedWalletName: INITIAL_STATE.walletName,
75
+ connectingWallet: false,
76
+ });
@@ -0,0 +1,16 @@
1
+ import { useWalletStore, WalletContext } from "./WalletContext";
2
+
3
+ export { WalletContext } from "./WalletContext";
4
+
5
+ interface Props {
6
+ children: React.ReactNode;
7
+ }
8
+
9
+ export const MeshProvider: React.FC<Props> = (props: any) => {
10
+ const store = useWalletStore();
11
+ return (
12
+ <WalletContext.Provider value={store}>
13
+ <>{props.children}</>
14
+ </WalletContext.Provider>
15
+ );
16
+ };
@@ -0,0 +1,9 @@
1
+ export * from "./useAddress";
2
+ export * from "./useAssets";
3
+ export * from "./useWalletList";
4
+ export * from "./useLovelace";
5
+ export * from "./useNetwork";
6
+ export * from "./useRewardAddress";
7
+ export * from "./useWallet";
8
+ export * from "./useWalletSubmit";
9
+ // export * from "./useWalletTx";
@@ -0,0 +1,21 @@
1
+ import { useContext, useEffect, useState } from "react";
2
+
3
+ import { WalletContext } from "../contexts";
4
+
5
+ export const useAddress = (accountId = 0) => {
6
+ const [address, setAddress] = useState<string>();
7
+ const { hasConnectedWallet, connectedWalletName, connectedWalletInstance } =
8
+ useContext(WalletContext);
9
+
10
+ useEffect(() => {
11
+ if (hasConnectedWallet) {
12
+ connectedWalletInstance.getUsedAddresses().then((addresses) => {
13
+ if (addresses[accountId]) {
14
+ setAddress(addresses[accountId]);
15
+ }
16
+ });
17
+ }
18
+ }, [accountId, connectedWalletName]);
19
+
20
+ return address;
21
+ };
@@ -0,0 +1,19 @@
1
+ import { useContext, useEffect, useState } from "react";
2
+
3
+ import type { Asset } from "@meshsdk/common";
4
+
5
+ import { WalletContext } from "../contexts";
6
+
7
+ export const useAssets = () => {
8
+ const [assets, setAssets] = useState<Asset[]>();
9
+ const { hasConnectedWallet, connectedWalletName, connectedWalletInstance } =
10
+ useContext(WalletContext);
11
+
12
+ useEffect(() => {
13
+ if (hasConnectedWallet) {
14
+ connectedWalletInstance.getAssets().then(setAssets);
15
+ }
16
+ }, [connectedWalletName]);
17
+
18
+ return assets;
19
+ };
@@ -0,0 +1,17 @@
1
+ import { useContext, useEffect, useState } from "react";
2
+
3
+ import { WalletContext } from "../contexts";
4
+
5
+ export const useLovelace = () => {
6
+ const [lovelace, setLovelace] = useState<string>();
7
+ const { hasConnectedWallet, connectedWalletName, connectedWalletInstance } =
8
+ useContext(WalletContext);
9
+
10
+ useEffect(() => {
11
+ if (hasConnectedWallet) {
12
+ connectedWalletInstance.getLovelace().then(setLovelace);
13
+ }
14
+ }, [connectedWalletName]);
15
+
16
+ return lovelace;
17
+ };
@@ -0,0 +1,17 @@
1
+ import { useContext, useEffect, useState } from "react";
2
+
3
+ import { WalletContext } from "../contexts";
4
+
5
+ export const useNetwork = () => {
6
+ const [networkId, setNetworkId] = useState<number>();
7
+ const { hasConnectedWallet, connectedWalletName, connectedWalletInstance } =
8
+ useContext(WalletContext);
9
+
10
+ useEffect(() => {
11
+ if (hasConnectedWallet) {
12
+ connectedWalletInstance.getNetworkId().then(setNetworkId);
13
+ }
14
+ }, [connectedWalletName]);
15
+
16
+ return networkId;
17
+ };
@@ -0,0 +1,21 @@
1
+ import { useContext, useEffect, useState } from "react";
2
+
3
+ import { WalletContext } from "../contexts";
4
+
5
+ export const useRewardAddress = (accountId = 0) => {
6
+ const [rewardAddress, setRewardAddress] = useState<string>();
7
+ const { hasConnectedWallet, connectedWalletName, connectedWalletInstance } =
8
+ useContext(WalletContext);
9
+
10
+ useEffect(() => {
11
+ if (hasConnectedWallet) {
12
+ connectedWalletInstance.getRewardAddresses().then((addresses) => {
13
+ if (addresses[accountId]) {
14
+ setRewardAddress(addresses[accountId]);
15
+ }
16
+ });
17
+ }
18
+ }, [accountId, connectedWalletName]);
19
+
20
+ return rewardAddress;
21
+ };
@@ -0,0 +1,31 @@
1
+ import { useContext } from "react";
2
+
3
+ import { WalletContext } from "../contexts";
4
+
5
+ export const useWallet = () => {
6
+ const {
7
+ hasConnectedWallet,
8
+ connectedWalletName,
9
+ connectedWalletInstance,
10
+ connectingWallet,
11
+ connectWallet,
12
+ disconnect,
13
+ error,
14
+ } = useContext(WalletContext);
15
+
16
+ if (connectWallet === undefined || disconnect === undefined) {
17
+ throw new Error(
18
+ "Can't call useWallet outside of the WalletProvider context",
19
+ );
20
+ }
21
+
22
+ return {
23
+ name: connectedWalletName,
24
+ connecting: connectingWallet,
25
+ connected: hasConnectedWallet,
26
+ wallet: connectedWalletInstance,
27
+ connect: connectWallet,
28
+ disconnect,
29
+ error,
30
+ };
31
+ };
@@ -0,0 +1,14 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ import type { Wallet } from "@meshsdk/common";
4
+ import { BrowserWallet } from "@meshsdk/wallet";
5
+
6
+ export const useWalletList = () => {
7
+ const [wallets, setWallets] = useState<Wallet[]>([]);
8
+
9
+ useEffect(() => {
10
+ setWallets(BrowserWallet.getInstalledWallets());
11
+ }, []);
12
+
13
+ return wallets;
14
+ };
@@ -0,0 +1,40 @@
1
+ import { useCallback, useContext, useState } from "react";
2
+
3
+ import { WalletContext } from "../contexts";
4
+
5
+ export const useWalletSubmit = () => {
6
+ const [error, setError] = useState<unknown>();
7
+ const [result, setResult] = useState<string>();
8
+ const [submitting, setSubmitting] = useState<boolean>(false);
9
+
10
+ const { hasConnectedWallet, connectedWalletInstance } =
11
+ useContext(WalletContext);
12
+
13
+ const submitTx = useCallback(async (signedTx: string) => {
14
+ setSubmitting(true);
15
+
16
+ try {
17
+ if (hasConnectedWallet) {
18
+ const txHash = await connectedWalletInstance.submitTx(signedTx);
19
+ setError(undefined);
20
+ setResult(txHash);
21
+ }
22
+
23
+ throw new Error(
24
+ "Please make sure to connect a wallet before calling useWalletSubmit",
25
+ );
26
+ } catch (error) {
27
+ setError(error);
28
+ console.error(error);
29
+ }
30
+
31
+ setSubmitting(false);
32
+ }, []);
33
+
34
+ return {
35
+ error,
36
+ result,
37
+ submitting,
38
+ submitTx,
39
+ };
40
+ };
@@ -0,0 +1,32 @@
1
+ // import { useContext, useState } from "react";
2
+ // import { Transaction } from "@meshsdk/core";
3
+ // import { WalletContext } from "../contexts";
4
+ // import type { Era, Protocol } from "@meshsdk/common";
5
+
6
+ // export const useWalletTx = (
7
+ // options: {
8
+ // era?: Era;
9
+ // parameters?: Protocol;
10
+ // } = {}
11
+ // ) => {
12
+ // const { era, parameters } = options;
13
+
14
+ // const { hasConnectedWallet, connectedWalletInstance } =
15
+ // useContext(WalletContext);
16
+
17
+ // const [tx] = useState<Transaction>(() => {
18
+ // if (hasConnectedWallet) {
19
+ // return new Transaction({
20
+ // initiator: connectedWalletInstance,
21
+ // parameters,
22
+ // era,
23
+ // });
24
+ // }
25
+
26
+ // throw new Error(
27
+ // "Please make sure to connect a wallet before calling useWalletTx"
28
+ // );
29
+ // });
30
+
31
+ // return tx;
32
+ // };
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./cardano-wallet";
2
+ export * from "./contexts";
3
+ export * from "./hooks";
4
+ export * from "./mesh-badge";
5
+ export * from "./stake-button";
@@ -0,0 +1,17 @@
1
+ import { MeshLogo } from "./mesh-logo";
2
+
3
+ export const MeshBadge = ({ isDark = false }) => (
4
+ <a
5
+ className={`ui-flex ui-max-w-fit ui-flex-col ui-items-center ui-rounded-md ui-border ui-border-solid ui-border-current ui-p-1 ui-text-xl ui-font-semibold ui-no-underline ${isDark ? `ui-bg-neutral-950 ui-text-neutral-50` : `ui-bg-neutral-50 ui-text-neutral-950`}`}
6
+ style={{
7
+ color: isDark ? "#EEEEEE" : "#111111",
8
+ backgroundColor: isDark ? "#111111" : "#EEEEEE",
9
+ }}
10
+ href="https://meshjs.dev/"
11
+ rel="noopener noreferrer"
12
+ target="_blank"
13
+ >
14
+ <MeshLogo />
15
+ Mesh
16
+ </a>
17
+ );
@@ -0,0 +1,10 @@
1
+ export const MeshLogo = () => (
2
+ <svg
3
+ className="ui-h-16 ui-p-2"
4
+ fill="currentColor"
5
+ viewBox="0 0 300 200"
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ >
8
+ <path d="m289 127-45-60-45-60c-.9-1.3-2.4-2-4-2s-3.1.7-4 2l-37 49.3c-2 2.7-6 2.7-8 0l-37-49.3c-.9-1.3-2.4-2-4-2s-3.1.7-4 2l-45 60-45 60c-1.3 1.8-1.3 4.2 0 6l45 60c.9 1.3 2.4 2 4 2s3.1-.7 4-2l37-49.3c2-2.7 6-2.7 8 0l37 49.3c.9 1.3 2.4 2 4 2s3.1-.7 4-2l37-49.3c2-2.7 6-2.7 8 0l37 49.3c.9 1.3 2.4 2 4 2s3.1-.7 4-2l45-60c1.3-1.8 1.3-4.2 0-6zm-90-103.3 32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0l-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0zm-90 0 32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0l-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0zm-53 152.6-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0l32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0zm90 0-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0l32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0zm90 0-32.5-43.3c-1.3-1.8-1.3-4.2 0-6l32.5-43.3c2-2.7 6-2.7 8 0l32.5 43.3c1.3 1.8 1.3 4.2 0 6l-32.5 43.3c-2 2.7-6 2.7-8 0z" />
9
+ </svg>
10
+ );