first commit

This commit is contained in:
Firesieht 2022-06-12 01:55:07 +03:00
parent 013f71c0da
commit 5eb2b68299
14 changed files with 167 additions and 376 deletions

94
package-lock.json generated
View File

@ -2013,6 +2013,30 @@
"source-map": "^0.7.3" "source-map": "^0.7.3"
} }
}, },
"@react-three/fiber": {
"version": "8.0.22",
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.0.22.tgz",
"integrity": "sha512-HkQR+SKm0Bi4qq9sxCV2rv2aXxEhWPcj52bLOCiW3WpF8sGJtmdc/pP4ih5gXwN7I5NMTa2eDeoSYkIPOwt1/g==",
"requires": {
"@babel/runtime": "^7.17.8",
"@types/react-reconciler": "^0.26.7",
"react-reconciler": "^0.27.0",
"react-use-measure": "^2.1.1",
"scheduler": "^0.21.0",
"suspend-react": "^0.0.8",
"zustand": "^3.7.1"
},
"dependencies": {
"scheduler": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
"requires": {
"loose-envify": "^1.1.0"
}
}
}
},
"@reduxjs/toolkit": { "@reduxjs/toolkit": {
"version": "1.8.2", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.2.tgz", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.2.tgz",
@ -2622,6 +2646,14 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/react-reconciler": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
"integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
"requires": {
"@types/react": "*"
}
},
"@types/resolve": { "@types/resolve": {
"version": "1.17.1", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
@ -2678,6 +2710,15 @@
"@types/jest": "*" "@types/jest": "*"
} }
}, },
"@types/three": {
"version": "0.141.0",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.141.0.tgz",
"integrity": "sha512-OJdKDgTPVBUgc+s74DYoy4aLznbFFC38Xm4ElmU1YwGNgR7GGFVvFCX7lpVgOsT6S1zSJtGdajTsOYE8/xY9nA==",
"dev": true,
"requires": {
"@types/webxr": "*"
}
},
"@types/trusted-types": { "@types/trusted-types": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
@ -2688,6 +2729,12 @@
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
}, },
"@types/webxr": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.4.0.tgz",
"integrity": "sha512-LQvrACV3Pj17GpkwHwXuTd733gfY+D7b9mKdrTmLdO7vo7P/o6209Qqtk63y/FCv/lspdmi0pWz6Qe/ull9kQg==",
"dev": true
},
"@types/ws": { "@types/ws": {
"version": "8.5.3", "version": "8.5.3",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
@ -4287,6 +4334,11 @@
"whatwg-url": "^8.0.0" "whatwg-url": "^8.0.0"
} }
}, },
"debounce": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"debug": { "debug": {
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -9703,6 +9755,25 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"react-reconciler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
"integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
"requires": {
"loose-envify": "^1.1.0",
"scheduler": "^0.21.0"
},
"dependencies": {
"scheduler": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
"requires": {
"loose-envify": "^1.1.0"
}
}
}
},
"react-redux": { "react-redux": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz",
@ -9783,6 +9854,14 @@
"workbox-webpack-plugin": "^6.4.1" "workbox-webpack-plugin": "^6.4.1"
} }
}, },
"react-use-measure": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
"integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
"requires": {
"debounce": "^1.2.1"
}
},
"read-cache": { "read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -10714,6 +10793,11 @@
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
}, },
"suspend-react": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
"integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg=="
},
"svg-parser": { "svg-parser": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
@ -10862,6 +10946,11 @@
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
}, },
"three": {
"version": "0.141.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.141.0.tgz",
"integrity": "sha512-JaSDAPWuk4RTzG5BYRQm8YZbERUxTfTDVouWgHMisS2to4E5fotMS9F2zPFNOIJyEFTTQDDKPpsgZVThKU3pXA=="
},
"throat": { "throat": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
@ -11816,6 +11905,11 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
},
"zustand": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA=="
} }
} }
} }

View File

@ -3,6 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@react-three/fiber": "^8.0.22",
"@reduxjs/toolkit": "^1.8.2", "@reduxjs/toolkit": "^1.8.2",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",
@ -15,6 +16,7 @@
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"react-redux": "^8.0.2", "react-redux": "^8.0.2",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"three": "^0.141.0",
"typescript": "^4.7.3", "typescript": "^4.7.3",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
@ -41,5 +43,8 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"@types/three": "^0.141.0"
} }
} }

View File

@ -1,39 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-float infinite 3s ease-in-out;
}
}
.App-header {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
}
.App-link {
color: rgb(112, 76, 182);
}
@keyframes App-logo-float {
0% {
transform: translateY(0);
}
50% {
transform: translateY(10px);
}
100% {
transform: translateY(0px);
}
}

View File

@ -1,15 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(
<Provider store={store}>
<App />
</Provider>
);
expect(getByText(/learn/i)).toBeInTheDocument();
});

View File

@ -1,56 +1,10 @@
import React from 'react'; import React from 'react';
import logo from './logo.svg'; import { Board } from './components/board';
import { Counter } from './features/counter/Counter';
import './App.css';
function App() { function App() {
return ( return (
<div className="App"> <div className="App">
<header className="App-header"> <Board></Board>
<img src={logo} className="App-logo" alt="logo" />
<Counter />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<span>
<span>Learn </span>
<a
className="App-link"
href="https://reactjs.org/"
target="_blank"
rel="noopener noreferrer"
>
React
</a>
<span>, </span>
<a
className="App-link"
href="https://redux.js.org/"
target="_blank"
rel="noopener noreferrer"
>
Redux
</a>
<span>, </span>
<a
className="App-link"
href="https://redux-toolkit.js.org/"
target="_blank"
rel="noopener noreferrer"
>
Redux Toolkit
</a>
,<span> and </span>
<a
className="App-link"
href="https://react-redux.js.org/"
target="_blank"
rel="noopener noreferrer"
>
React Redux
</a>
</span>
</header>
</div> </div>
); );
} }

View File

@ -1,9 +1,7 @@
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
counter: counterReducer,
}, },
}); });

44
src/components/board.tsx Normal file
View File

@ -0,0 +1,44 @@
import React, { useEffect } from "react";
import * as THREE from 'three';
import { Canvas, RootState, useFrame, useThree } from '@react-three/fiber'
import { Cell } from "./cell";
function ThreeSettings(){
const set = useThree((state)=>state.set)
const cam = useThree((state)=>state.camera)
useEffect(
()=>{
cam.far = 500
cam.near = 0.1
cam.updateProjectionMatrix();
cam.rotation.set(-Math.PI/4,0,0)
cam.position.set(0,6,7)
set({
size:{width:512, height:512},
})
}
)
return(null);
}
export const Board:React.FC = () =>{
let cells = new Array<JSX.Element>()
let color = true
for (let i = 0; i<64; i++){
cells.push(
<Cell color={color? "white":"black"} position={[i%9 - 4,1,Math.floor(i/9)]}></Cell>
)
color = !color
}
return(
<Canvas>
<ThreeSettings/>
<ambientLight />
<pointLight position={[10, 10, 10]} />
{
cells
}
</Canvas>
);
}

22
src/components/cell.tsx Normal file
View File

@ -0,0 +1,22 @@
import React, { useRef, useState } from "react";
import { Canvas, useFrame, Vector3 } from '@react-three/fiber'
interface CellIE{
position:Vector3
color: string
}
export const Cell:React.FC<CellIE> = (props:CellIE) => {
const mesh = useRef()
return(
<mesh
position={props.position}
ref={mesh as any}
scale={1}>
<boxGeometry args={[1, 0.5, 1]} />
<meshStandardMaterial color={props.color} />
</mesh>
);
}

View File

@ -1,79 +0,0 @@
.row {
display: flex;
align-items: center;
justify-content: center;
}
.row > button {
margin-left: 4px;
margin-right: 8px;
}
.row:not(:last-child) {
margin-bottom: 16px;
}
.value {
font-size: 78px;
padding-left: 16px;
padding-right: 16px;
margin-top: 2px;
font-family: 'Courier New', Courier, monospace;
}
.button {
appearance: none;
background: none;
font-size: 32px;
padding-left: 12px;
padding-right: 12px;
outline: none;
border: 2px solid transparent;
color: rgb(112, 76, 182);
padding-bottom: 4px;
cursor: pointer;
background-color: rgba(112, 76, 182, 0.1);
border-radius: 2px;
transition: all 0.15s;
}
.textbox {
font-size: 32px;
padding: 2px;
width: 64px;
text-align: center;
margin-right: 4px;
}
.button:hover,
.button:focus {
border: 2px solid rgba(112, 76, 182, 0.4);
}
.button:active {
background-color: rgba(112, 76, 182, 0.2);
}
.asyncButton {
composes: button;
position: relative;
}
.asyncButton:after {
content: '';
background-color: rgba(112, 76, 182, 0.15);
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
opacity: 0;
transition: width 1s linear, opacity 0.5s ease 1s;
}
.asyncButton:active:after {
width: 0%;
opacity: 1;
transition: 0s;
}

View File

@ -1,68 +0,0 @@
import React, { useState } from 'react';
import { useAppSelector, useAppDispatch } from '../../app/hooks';
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
incrementIfOdd,
selectCount,
} from './counterSlice';
import styles from './Counter.module.css';
export function Counter() {
const count = useAppSelector(selectCount);
const dispatch = useAppDispatch();
const [incrementAmount, setIncrementAmount] = useState('2');
const incrementValue = Number(incrementAmount) || 0;
return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
<span className={styles.value}>{count}</span>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
</div>
<div className={styles.row}>
<input
className={styles.textbox}
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button
className={styles.button}
onClick={() => dispatch(incrementByAmount(incrementValue))}
>
Add Amount
</button>
<button
className={styles.asyncButton}
onClick={() => dispatch(incrementAsync(incrementValue))}
>
Add Async
</button>
<button
className={styles.button}
onClick={() => dispatch(incrementIfOdd(incrementValue))}
>
Add If Odd
</button>
</div>
</div>
);
}

View File

@ -1,6 +0,0 @@
// A mock function to mimic making an async request for data
export function fetchCount(amount = 1) {
return new Promise<{ data: number }>((resolve) =>
setTimeout(() => resolve({ data: amount }), 500)
);
}

View File

@ -1,34 +0,0 @@
import counterReducer, {
CounterState,
increment,
decrement,
incrementByAmount,
} from './counterSlice';
describe('counter reducer', () => {
const initialState: CounterState = {
value: 3,
status: 'idle',
};
it('should handle initial state', () => {
expect(counterReducer(undefined, { type: 'unknown' })).toEqual({
value: 0,
status: 'idle',
});
});
it('should handle increment', () => {
const actual = counterReducer(initialState, increment());
expect(actual.value).toEqual(4);
});
it('should handle decrement', () => {
const actual = counterReducer(initialState, decrement());
expect(actual.value).toEqual(2);
});
it('should handle incrementByAmount', () => {
const actual = counterReducer(initialState, incrementByAmount(2));
expect(actual.value).toEqual(5);
});
});

View File

@ -1,84 +0,0 @@
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState, AppThunk } from '../../app/store';
import { fetchCount } from './counterAPI';
export interface CounterState {
value: number;
status: 'idle' | 'loading' | 'failed';
}
const initialState: CounterState = {
value: 0,
status: 'idle',
};
// The function below is called a thunk and allows us to perform async logic. It
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
// will call the thunk with the `dispatch` function as the first argument. Async
// code can then be executed and other actions can be dispatched. Thunks are
// typically used to make async requests.
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
async (amount: number) => {
const response = await fetchCount(amount);
// The value we return becomes the `fulfilled` action payload
return response.data;
}
);
export const counterSlice = createSlice({
name: 'counter',
initialState,
// The `reducers` field lets us define reducers and generate associated actions
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
// The `extraReducers` field lets the slice handle actions defined elsewhere,
// including actions generated by createAsyncThunk or in other slices.
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = 'loading';
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'idle';
state.value += action.payload;
})
.addCase(incrementAsync.rejected, (state) => {
state.status = 'failed';
});
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
export const selectCount = (state: RootState) => state.counter.value;
// We can also write thunks by hand, which may contain both sync and async logic.
// Here's an example of conditionally dispatching actions based on current state.
export const incrementIfOdd =
(amount: number): AppThunk =>
(dispatch, getState) => {
const currentValue = selectCount(getState());
if (currentValue % 2 === 1) {
dispatch(incrementByAmount(amount));
}
};
export default counterSlice.reducer;

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g fill="#764ABC"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB