Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67046384fc | ||
|
|
bf120f8928 | ||
|
|
813e5b57ae | ||
|
|
0943fc80ff | ||
|
|
2d9dbdd49e | ||
|
|
f9af5e0216 | ||
|
|
98741fb49c | ||
|
|
e863523510 | ||
|
|
4ff9d6ba5c | ||
|
|
7da654649d | ||
|
|
195e3f9a5a | ||
|
|
4e3f3bb644 | ||
|
|
6b4e6b1566 | ||
|
|
550774e291 | ||
|
|
9a495fd902 | ||
|
|
70de4ec769 | ||
|
|
02abf3417b | ||
|
|
148a953a7d | ||
|
|
c655c24007 | ||
|
|
22e727ee73 | ||
|
|
3dba75d8aa | ||
|
|
094369c63a | ||
|
|
ddb07d31aa | ||
|
|
9adaeb6fed | ||
|
|
3b1a686f99 | ||
|
|
fa4c451c8a | ||
|
|
b59b504d8b | ||
|
|
74a94d42c1 | ||
|
|
cc0c5c67f9 | ||
|
|
f9b9220c8b | ||
|
|
f6b7ca3a7e | ||
|
|
70cb660cd2 | ||
|
|
92ff3bd4dc | ||
|
|
edb4beefdd | ||
|
|
10f3aa030e | ||
|
|
51df6dbb4c | ||
|
|
23e727b97a | ||
|
|
b5393212e9 | ||
|
|
88b2697170 | ||
|
|
cbc0e06d4f | ||
|
|
cb8340e734 | ||
|
|
80b6f7afb5 | ||
|
|
0093a99312 | ||
|
|
3f75d6bcfc | ||
|
|
4f6aa69009 | ||
|
|
7afc447e69 | ||
|
|
8aa1347f7d | ||
|
|
9d66ba076b | ||
|
|
4b93ecb357 | ||
|
|
367145c9ff | ||
|
|
d43bd76631 | ||
|
|
d796765ef3 | ||
|
|
29b9734a56 | ||
|
|
1323989f83 | ||
|
|
45be34b103 | ||
|
|
75ef1c2d39 | ||
|
|
ed918fc4f9 | ||
|
|
aba5336bdb | ||
|
|
2ac9665f11 | ||
|
|
452f779295 | ||
|
|
a159ac946c | ||
|
|
47d5316480 | ||
|
|
b560d62194 | ||
|
|
19384e1027 | ||
|
|
a43b5a6a02 | ||
|
|
90ffafd4ad | ||
|
|
024c19f203 | ||
|
|
abac11dfb4 | ||
|
|
b22388770d | ||
|
|
e3538d66d9 | ||
|
|
7c96decfc9 | ||
|
|
5dc7997dc5 | ||
|
|
02ffce3ca5 | ||
|
|
09200d4632 | ||
|
|
762a698be2 | ||
|
|
6c24833a91 | ||
|
|
3cf4827e4d | ||
|
|
85e7b52493 | ||
|
|
ea32fe1c2d | ||
|
|
78451db766 | ||
|
|
15b146fab6 | ||
|
|
67a68469db | ||
|
|
f1715cede9 | ||
|
|
4d3dcc0977 | ||
|
|
ef5bf58108 | ||
|
|
b1dad60260 | ||
|
|
7e3627a7e1 | ||
|
|
c203e6f497 | ||
|
|
e3b78a860d | ||
|
|
e52b72f731 | ||
|
|
0d5a0fe21e | ||
|
|
39462bfc14 | ||
|
|
cadab15871 | ||
|
|
6dd97428c7 | ||
|
|
274fd1a299 | ||
|
|
823273bc26 | ||
|
|
21e2161bfd | ||
|
|
a7e9b1f9e3 | ||
|
|
08791fc13d | ||
|
|
7d0c405b8d | ||
|
|
291475afcc | ||
|
|
c53e5c50da | ||
|
|
181faba3a6 | ||
|
|
8a3cf6ce48 | ||
|
|
86ef94af34 | ||
|
|
b88f7e73da |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@status-im/js': patch
|
||||
---
|
||||
|
||||
get peer based on environment
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@status-im/js': patch
|
||||
---
|
||||
|
||||
use built-in crypto for pbkdf2
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@status-im/js": patch
|
||||
---
|
||||
|
||||
Update encode-url-data.ts
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
types: [opened, synchronize]
|
||||
|
||||
env:
|
||||
NEXT_PUBLIC_GHOST_API_URL: ''
|
||||
NEXT_PUBLIC_GHOST_API_KEY: ''
|
||||
INFURA_API_KEY: ''
|
||||
TAMAGUI_TARGET: 'web'
|
||||
|
||||
@@ -5,6 +5,14 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
env:
|
||||
NEXT_PUBLIC_GHOST_API_URL: ''
|
||||
NEXT_PUBLIC_GHOST_API_KEY: ''
|
||||
INFURA_API_KEY: ''
|
||||
TAMAGUI_TARGET: 'web'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -12,8 +20,12 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn build
|
||||
- run: yarn typecheck
|
||||
- run: yarn lint && yarn format --check
|
||||
- run: yarn test
|
||||
- uses: changesets/action@v1
|
||||
id: changesets
|
||||
with:
|
||||
|
||||
@@ -104,3 +104,6 @@ web-build/
|
||||
|
||||
# Local
|
||||
**/.data
|
||||
|
||||
# Contentlayer
|
||||
.contentlayer
|
||||
|
||||
@@ -26,14 +26,15 @@
|
||||
"smartStep": true,
|
||||
"sourceMaps": true,
|
||||
"env": {
|
||||
"DEBUG": "*",
|
||||
// "DEBUG": "*,-vite*,-connect:*",
|
||||
"DEBUG_HIDE_DATE": "0",
|
||||
"DEBUG_COLORS": "1",
|
||||
"VITE_NODE": "true"
|
||||
},
|
||||
"runtimeArgs": ["--preserve-symlinks"],
|
||||
"outFiles": ["${workspaceFolder}/**/*.js"],
|
||||
"resolveSourceMapLocations": ["**/*"]
|
||||
"resolveSourceMapLocations": ["**/*"],
|
||||
"outputCapture": "std"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
@@ -59,48 +60,6 @@
|
||||
"smartStep": true,
|
||||
"console": "integratedTerminal",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Launch Website via Next.js (server-side)",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/apps/website",
|
||||
"command": "yarn dev"
|
||||
},
|
||||
{
|
||||
"name": "Attach to Website via Next.js (client-side)",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"webRoot": "${workspaceFolder}/apps/website",
|
||||
"url": "http://localhost:3000",
|
||||
"skipFiles": [
|
||||
".next/**",
|
||||
"${workspaceFolder}/node_modules/**",
|
||||
"<node_internals>/**",
|
||||
"**/webpack-internal://**"
|
||||
],
|
||||
"runtimeArgs": ["--auto-open-devtools-for-tabs"]
|
||||
},
|
||||
// todo: consider https://code.visualstudio.com/docs/editor/debugging#_compound-launch-configurations instead
|
||||
// todo: consider client+prelaunch as full stack
|
||||
// todo: consider workspaces https://code.visualstudio.com/docs/editor/multi-root-workspaces#_debugging
|
||||
{
|
||||
"name": "Launch Website via Next.js (full stack)",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}/apps/website",
|
||||
"command": "yarn dev -p 3000",
|
||||
"serverReadyAction": {
|
||||
"pattern": "started server on .+, url: (https?://.+)",
|
||||
"action": "startDebugging",
|
||||
"name": "Attach to Website via Next.js (client-side)",
|
||||
"killOnServerStop": false
|
||||
},
|
||||
"skipFiles": [
|
||||
".next/**",
|
||||
"${workspaceFolder}/node_modules/**",
|
||||
"<node_internals>/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Status Web
|
||||
# Status Web 🌐
|
||||
|
||||
[](https://github.com/status-im/status-web/actions/workflows/ci.yml)
|
||||
|
||||
@@ -125,3 +125,7 @@ And run it:
|
||||
```sh
|
||||
yarn workspace with-vite run dev
|
||||
```
|
||||
|
||||
## Sponsors
|
||||
|
||||
This project is sponsored by Browserstack.
|
||||
|
||||
@@ -23,5 +23,14 @@
|
||||
"@vitejs/plugin-react": "^3.1.0",
|
||||
"typescript": "^5.0.3",
|
||||
"vite": "^4.2.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,jsx,mjs}": [
|
||||
"eslint",
|
||||
"prettier --write"
|
||||
],
|
||||
"*.{md,mdx,yml,yaml,json}": [
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.17.9",
|
||||
"@expo/metro-config": "^0.3.21",
|
||||
"@tamagui/babel-plugin": "1.11.1",
|
||||
"@tamagui/babel-plugin": "1.74.21",
|
||||
"@types/react-native": "~0.70.6",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"babel-plugin-transform-inline-environment-variables": "^0.4.4",
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
"plugin:tailwindcss/recommended",
|
||||
"next",
|
||||
"next/core-web-vitals"
|
||||
]
|
||||
],
|
||||
"rules": {
|
||||
"@next/next/no-html-link-for-pages": "off"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,19 +13,30 @@
|
||||
"dependencies": {
|
||||
"@status-im/components": "*",
|
||||
"@status-im/icons": "*",
|
||||
"@tamagui/core": "1.11.1",
|
||||
"@tamagui/core": "1.74.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-native-web": "^0.18.12",
|
||||
"use-resize-observer": "^9.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tamagui/vite-plugin": "1.11.1",
|
||||
"@status-im/eslint-config": "*",
|
||||
"@tamagui/vite-plugin": "1.74.21",
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@vitejs/plugin-react-swc": "^3.2.0",
|
||||
"@status-im/eslint-config": "*",
|
||||
"next": "^13.4.12",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"typescript": "^5.0.3",
|
||||
"vite": "^4.2.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,jsx,mjs}": [
|
||||
"eslint",
|
||||
"prettier --write"
|
||||
],
|
||||
"*.{md,mdx,yml,yaml,json}": [
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
IGNORE_TS_CONFIG_PATHS=true
|
||||
TAMAGUI_TARGET=web
|
||||
TAMAGUI_DISABLE_WARN_DYNAMIC_LOAD=1
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"@status-im/eslint-config",
|
||||
"plugin:tailwindcss/recommended",
|
||||
"next",
|
||||
"next/core-web-vitals"
|
||||
]
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
!.env.*
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
# Tamagui
|
||||
.tamagui
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "avoid",
|
||||
"tailwindConfig": "./tailwind.config.cjs"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import type { clientEnv } from './src/config/env.client.mjs'
|
||||
import type { serverEnv } from './src/config/env.server.mjs'
|
||||
|
||||
type Env = typeof clientEnv & typeof serverEnv
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
interface ProcessEnv extends Env {}
|
||||
/* eslint-enable @typescript-eslint/no-empty-interface */
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@@ -1,76 +0,0 @@
|
||||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
/* eslint-disable import/default */
|
||||
|
||||
import './src/config/env.server.mjs'
|
||||
import './src/config/env.client.mjs'
|
||||
|
||||
import tamagui_next_plugin from '@tamagui/next-plugin'
|
||||
import { join } from 'node:path'
|
||||
|
||||
const { withTamagui } = tamagui_next_plugin
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
let config = {
|
||||
// output: 'export',
|
||||
reactStrictMode: true,
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
images: {
|
||||
// disableStaticImages: true,
|
||||
},
|
||||
transpilePackages: [
|
||||
// 'react-native',
|
||||
'react-native-web',
|
||||
// 'expo-modules-core',
|
||||
'expo-blur',
|
||||
// '@status-im/components',
|
||||
// '@status-im/js',
|
||||
],
|
||||
experimental: {
|
||||
legacyBrowsers: false,
|
||||
// esmExternals: 'loose',
|
||||
scrollRestoration: true,
|
||||
},
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
withTamagui({
|
||||
config: './tamagui.config.ts',
|
||||
components: [
|
||||
// fixme?: works without it
|
||||
// '@status-im/icons',
|
||||
'@status-im/components',
|
||||
// './node_modules/@status-im/components/packages/components/dist',
|
||||
],
|
||||
importsWhitelist: ['constants.js', 'colors.js'],
|
||||
logTimings: true,
|
||||
disableExtraction: true,
|
||||
// experiment - reduced bundle size react-native-web
|
||||
useReactNativeWebLite: false,
|
||||
shouldExtract: path => {
|
||||
if (path.includes(join('packages', 'app'))) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
excludeReactNativeWebExports: [
|
||||
'Switch',
|
||||
'ProgressBar',
|
||||
'Picker',
|
||||
'CheckBox',
|
||||
'Touchable',
|
||||
'Modal',
|
||||
],
|
||||
}),
|
||||
]
|
||||
|
||||
export default () => {
|
||||
for (const plugin of plugins) {
|
||||
config = {
|
||||
...config,
|
||||
...plugin(config),
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"name": "website",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc",
|
||||
"clean": "rimraf .next .tamagui .turbo .vercel/output node_modules",
|
||||
"preview": "next start --port 8151"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.0.3",
|
||||
"@radix-ui/react-navigation-menu": "^1.1.2",
|
||||
"@react-spring/web": "^9.7.2",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@status-im/colors": "*",
|
||||
"@status-im/components": "*",
|
||||
"@status-im/icons": "*",
|
||||
"@status-im/js": "*",
|
||||
"@tamagui/next-theme": "1.11.1",
|
||||
"@tanstack/react-query": "^4.29.7",
|
||||
"@vercel/og": "^0.5.4",
|
||||
"@tryghost/content-api": "^1.11.13",
|
||||
"@visx/visx": "^2.18.0",
|
||||
"class-variance-authority": "^0.6.0",
|
||||
"d3-array": "^3.2.3",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"next": "13.2.4",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-native-web": "^0.18.12",
|
||||
"ts-pattern": "^4.3.0",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@achingbrain/ssdp": "^4.0.1",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@tamagui/next-plugin": "1.11.1",
|
||||
"@types/d3-array": "^3.0.4",
|
||||
"@types/d3-time-format": "^4.0.0",
|
||||
"@types/node": "^18.11.11",
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"@types/tryghost__content-api": "^1.3.11",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"@status-im/eslint-config": "*",
|
||||
"postcss": "^8.4.21",
|
||||
"rehype-parse": "^8.0.4",
|
||||
"rehype-react": "^7.2.0",
|
||||
"rehype-stringify": "^9.0.3",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"tailwindcss-animate": "^1.0.5",
|
||||
"typescript": "^5.0.3",
|
||||
"unified": "^10.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.x"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 310 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 642 KiB |
|
Before Width: | Height: | Size: 210 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g clip-path="url(#a)"><mask id="b" width="32" height="33" x="0" y="-1" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#fff" d="M16 0C4 0 0 4 0 16s4 16 16 16 16-4 16-16S28 0 16 0Z"/></mask><g mask="url(#b)"><g filter="url(#c)"><circle cx="23" cy="9" r="19" fill="#1992D7"/></g><g filter="url(#d)"><circle cx="33" cy="19" r="19" fill="#F6B03C"/></g><g filter="url(#e)"><circle cx="5" cy="31" r="19" fill="#FF7D46"/></g><g filter="url(#f)"><circle cx="-7" cy="9" r="19" fill="#7140FD"/></g><g fill="#fff" fill-rule="evenodd" clip-rule="evenodd" filter="url(#g)"><path d="M19.656 16.862c-.26 3.263-2.603 6.148-5.776 6.329-1.949.111-3.895-1.079-4-3.01-.082-1.556.88-2.69 2.56-3.125a6.04 6.04 0 0 1 1.212-.189c1.783-.1 2.9.308 4.683.207.411-.02.82-.082 1.219-.184.032-.009.07-.018.102-.028ZM12.352 15.137c.26-3.263 2.603-6.147 5.776-6.329 1.949-.11 3.895 1.08 4 3.011.085 1.555-.88 2.69-2.56 3.125a6.04 6.04 0 0 1-1.212.189c-1.783.1-2.9-.308-4.683-.209a6.24 6.24 0 0 0-1.219.184l-.102.03Z"/></g></g></g><defs><filter id="c" width="56.861" height="56.861" x="-5.43" y="-19.431" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="d" width="56.861" height="56.861" x="4.57" y="-9.431" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="e" width="56.861" height="56.861" x="-23.43" y="2.569" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="f" width="56.861" height="56.861" x="-35.43" y="-19.431" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="g" width="72.456" height="74.596" x="-20.224" y="-13.271" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="8.026"/><feGaussianBlur stdDeviation="15.05"/><feColorMatrix values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_718_92851"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_718_92851" result="shape"/></filter><clipPath id="a"><path fill="#fff" d="M0 0h32v32H0z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g clip-path="url(#a)"><mask id="b" width="32" height="33" x="0" y="-1" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#fff" d="M16 0C4 0 0 4 0 16s4 16 16 16 16-4 16-16S28 0 16 0Z"/></mask><g mask="url(#b)"><g filter="url(#c)"><circle cx="23" cy="9" r="19" fill="#1992D7"/></g><g filter="url(#d)"><circle cx="33" cy="19" r="19" fill="#F6B03C"/></g><g filter="url(#e)"><circle cx="5" cy="31" r="19" fill="#FF7D46"/></g><g filter="url(#f)"><circle cx="-7" cy="9" r="19" fill="#7140FD"/></g><g fill="#fff" fill-rule="evenodd" clip-rule="evenodd" filter="url(#g)"><path d="M19.656 16.862c-.26 3.263-2.603 6.148-5.776 6.329-1.949.111-3.895-1.079-4-3.01-.082-1.556.88-2.69 2.56-3.125a6.04 6.04 0 0 1 1.212-.189c1.783-.1 2.9.308 4.683.207.411-.02.82-.082 1.219-.184.032-.009.07-.018.102-.028ZM12.352 15.137c.26-3.263 2.603-6.147 5.776-6.329 1.949-.11 3.895 1.08 4 3.011.085 1.555-.88 2.69-2.56 3.125a6.04 6.04 0 0 1-1.212.189c-1.783.1-2.9-.308-4.683-.209a6.24 6.24 0 0 0-1.219.184l-.102.03Z"/></g></g></g><defs><filter id="c" width="56.861" height="56.861" x="-5.43" y="-19.431" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="d" width="56.861" height="56.861" x="4.57" y="-9.431" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="e" width="56.861" height="56.861" x="-23.43" y="2.569" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="f" width="56.861" height="56.861" x="-35.43" y="-19.431" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92851" stdDeviation="4.715"/></filter><filter id="g" width="72.456" height="74.596" x="-20.224" y="-13.271" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="8.026"/><feGaussianBlur stdDeviation="15.05"/><feColorMatrix values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_718_92851"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_718_92851" result="shape"/></filter><clipPath id="a"><path fill="#fff" d="M0 0h32v32H0z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g clip-path="url(#a)"><mask id="b" width="32" height="32" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#fff" d="M16 0C4 0 0 4 0 16s4 16 16 16 16-4 16-16S28 0 16 0Z"/></mask><g mask="url(#b)"><g filter="url(#c)"><circle cx="23" cy="9" r="19" fill="#2A4AF5" fill-opacity=".4"/></g><g filter="url(#d)"><circle cx="33" cy="19" r="19" fill="#2A4AF5" fill-opacity=".4"/></g><g filter="url(#e)"><circle cx="5" cy="31" r="19" fill="#2A4AF5" fill-opacity=".4"/></g><g filter="url(#f)"><circle cx="-7" cy="9" r="19" fill="#2A4AF5" fill-opacity=".4"/></g><g stroke="#fff" stroke-width=".5" filter="url(#g)"><path d="M19.532 16.863c-.259 3.253-2.59 6.13-5.75 6.31-1.939.111-3.876-1.075-3.98-3.001-.081-1.55.876-2.682 2.547-3.116.395-.103.8-.166 1.207-.188 1.775-.1 2.886.308 4.661.207.41-.021.816-.083 1.213-.184.032-.009.07-.018.102-.028ZM12.266 15.143c.259-3.254 2.591-6.13 5.75-6.31 1.939-.111 3.876 1.075 3.98 3.001.085 1.55-.876 2.682-2.547 3.116a6 6 0 0 1-1.207.189c-1.775.1-2.886-.308-4.661-.208-.41.02-.816.082-1.213.183l-.102.03Z" clip-rule="evenodd"/><path d="M16 1C4.75 1 1 4.75 1 16s3.75 15 15 15 15-3.75 15-15S27.25 1 16 1Z" opacity=".5"/></g></g></g><defs><filter id="c" width="58" height="58" x="-6" y="-20" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92063" stdDeviation="5"/></filter><filter id="d" width="58" height="58" x="4" y="-10" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92063" stdDeviation="5"/></filter><filter id="e" width="58" height="58" x="-24" y="2" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92063" stdDeviation="5"/></filter><filter id="f" width="58" height="58" x="-36" y="-20" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_92063" stdDeviation="5"/></filter><filter id="g" width="90.5" height="90.5" x="-29.25" y="-21.25" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="8"/><feGaussianBlur stdDeviation="15"/><feColorMatrix values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_718_92063"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_718_92063" result="shape"/></filter><clipPath id="a"><path fill="#fff" d="M0 0h32v32H0z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none"><g clip-path="url(#a)"><mask id="b" width="32" height="33" x="0" y="-1" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#fff" d="M16 0C4 0 0 4 0 16s4 16 16 16 16-4 16-16S28 0 16 0Z"/></mask><g mask="url(#b)"><g filter="url(#c)"><circle cx="23" cy="9" r="19" fill="#EC266C"/></g><g filter="url(#d)"><circle cx="33" cy="19" r="19" fill="#F66F8F"/></g><g filter="url(#e)"><circle cx="5" cy="31" r="19" fill="#EC266C"/></g><g filter="url(#f)"><circle cx="-7" cy="9" r="19" fill="#7140FD"/></g><g filter="url(#g)"><path fill="#fff" d="M23.787 15.315c-.223 3.894-3.781 6.765-7.792 7.085-4.01-.32-7.568-3.19-7.79-7.085-.137-2.392 1.325-4.78 3.7-4.91 1.91-.1 3.304 1.082 3.839 3.142.127.487.205.985.232 1.488.006.102.01.203.013.302.011-.096.02-.197.026-.302a7.399 7.399 0 0 1 .232-1.488c.534-2.06 1.929-3.241 3.84-3.141 2.374.129 3.836 2.517 3.7 4.909Z"/></g></g></g><defs><filter id="c" width="63.147" height="63.147" x="-8.574" y="-22.574" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_91701" stdDeviation="6.287"/></filter><filter id="d" width="63.147" height="63.147" x="1.426" y="-12.574" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_91701" stdDeviation="6.287"/></filter><filter id="e" width="63.147" height="63.147" x="-26.574" y="-.574" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_91701" stdDeviation="6.287"/></filter><filter id="f" width="63.147" height="63.147" x="-38.574" y="-22.574" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_718_91701" stdDeviation="6.287"/></filter><filter id="g" width="75.602" height="72" x="-21.805" y="-11.6" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="8"/><feGaussianBlur stdDeviation="15"/><feColorMatrix values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_718_91701"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_718_91701" result="shape"/></filter><clipPath id="a"><path fill="#fff" d="M0 0h32v32H0z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
@@ -1,22 +0,0 @@
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_1111_39343)">
|
||||
<circle cx="10" cy="10" r="10" fill="#647084" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11.5625 19.8785C11.0534 19.9584 10.5315 19.9999 10 19.9999C9.46846 19.9999 8.94661 19.9584 8.4375 19.8785V12.9694H5.89844V10.0611H8.4375V7.84446C8.4375 5.32289 9.93044 3.93005 12.2146 3.93005C13.3087 3.93005 14.4531 4.12656 14.4531 4.12656V6.60254H13.1921C11.9499 6.60254 11.5625 7.37809 11.5625 8.17376V10.0611H14.3359L13.8926 12.9694H11.5625V19.8785Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1111_39343">
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 790 B |
@@ -1,19 +0,0 @@
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_1111_39344)">
|
||||
<path
|
||||
d="M11.865 12.79C11.9358 12.86 11.9358 12.9742 11.865 13.045C11.4775 13.43 10.87 13.6175 10.0058 13.6175L9.99917 13.6158L9.9925 13.6175C9.12917 13.6175 8.52083 13.43 8.13333 13.0442C8.0625 12.9742 8.0625 12.86 8.13333 12.79C8.20333 12.72 8.31833 12.72 8.38917 12.79C8.705 13.1042 9.22917 13.2575 9.9925 13.2575L9.99917 13.2592L10.0058 13.2575C10.7683 13.2575 11.2925 13.1042 11.6092 12.79C11.68 12.72 11.795 12.72 11.865 12.79ZM8.99833 10.775C8.99833 10.3525 8.65333 10.0092 8.23 10.0092C7.80583 10.0092 7.46083 10.3525 7.46083 10.775C7.46083 11.1967 7.80583 11.54 8.23 11.54C8.65333 11.5408 8.99833 11.1975 8.99833 10.775ZM20 10C20 15.5225 15.5225 20 10 20C4.4775 20 0 15.5225 0 10C0 4.4775 4.4775 0 10 0C15.5225 0 20 4.4775 20 10ZM15.8333 9.8925C15.8333 9.18333 15.2542 8.60667 14.5417 8.60667C14.1942 8.60667 13.8792 8.74583 13.6467 8.96917C12.7667 8.39 11.5758 8.02167 10.2583 7.97417L10.9792 5.70417L12.9317 6.16167L12.9292 6.19C12.9292 6.77 13.4033 7.24167 13.9858 7.24167C14.5683 7.24167 15.0417 6.77 15.0417 6.19C15.0417 5.61 14.5683 5.13833 13.9858 5.13833C13.5383 5.13833 13.1575 5.4175 13.0033 5.80833L10.8992 5.315C10.8075 5.2925 10.7133 5.34583 10.685 5.43583L9.88083 7.9675C8.50083 7.98417 7.25167 8.35583 6.3325 8.95167C6.10167 8.73917 5.79583 8.60583 5.4575 8.60583C4.74583 8.60667 4.16667 9.18333 4.16667 9.8925C4.16667 10.3642 4.42583 10.7725 4.80667 10.9967C4.78167 11.1333 4.765 11.2725 4.765 11.4133C4.765 13.3142 7.1025 14.8608 9.97583 14.8608C12.8492 14.8608 15.1867 13.3142 15.1867 11.4133C15.1867 11.28 15.1725 11.1492 15.15 11.02C15.555 10.8025 15.8333 10.3817 15.8333 9.8925ZM11.7733 10.01C11.3492 10.01 11.005 10.3533 11.005 10.7758C11.005 11.1975 11.35 11.5408 11.7733 11.5408C12.1967 11.5408 12.5417 11.1975 12.5417 10.7758C12.5417 10.3533 12.1975 10.01 11.7733 10.01Z"
|
||||
fill="#647084"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1111_39344">
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1,108 +0,0 @@
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_1111_39335)">
|
||||
<g clip-path="url(#clip1_1111_39335)">
|
||||
<mask
|
||||
id="mask0_1111_39335"
|
||||
style="mask-type: alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="0"
|
||||
y="-1"
|
||||
width="20"
|
||||
height="21"
|
||||
>
|
||||
<path
|
||||
d="M10 -0.000366211C2.5 -0.000366211 0 2.49963 0 9.99963C0 17.4996 2.5 19.9996 10 19.9996C17.5 19.9996 20 17.4996 20 9.99963C20 2.49963 17.5 -0.000366211 10 -0.000366211Z"
|
||||
fill="white"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1111_39335)">
|
||||
<g filter="url(#filter0_f_1111_39335)">
|
||||
<circle cx="9.0625" cy="13.4375" r="17.8125" fill="#647084" />
|
||||
</g>
|
||||
<g filter="url(#filter1_d_1111_39335)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12.6063 10.6139C12.4204 12.9416 10.7481 14.9991 8.48319 15.1285C7.0925 15.2078 5.70345 14.3591 5.62842 12.9809C5.57025 11.8718 6.25689 11.0625 7.45512 10.7524C7.73822 10.6783 8.02824 10.6331 8.32063 10.6176C9.59334 10.5459 10.3903 10.8376 11.6631 10.7654C11.9567 10.7505 12.2481 10.7066 12.5329 10.6344C12.5558 10.6279 12.5835 10.6209 12.6063 10.6139Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M7.39258 9.38378C7.57851 7.05609 9.25081 4.99853 11.5157 4.86912C12.9064 4.78986 14.2954 5.63855 14.3705 7.01672C14.4314 8.12584 13.742 8.93517 12.5438 9.24574C12.2607 9.31992 11.9707 9.36509 11.6783 9.38054C10.4055 9.45225 9.60854 9.16055 8.33583 9.23173C8.0422 9.24659 7.75081 9.29049 7.46597 9.36275C7.44314 9.36976 7.41541 9.37677 7.39258 9.38378Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_f_1111_39335"
|
||||
x="-14.6439"
|
||||
y="-10.2689"
|
||||
width="47.4128"
|
||||
height="47.4128"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
>
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2.94696"
|
||||
result="effect1_foregroundBlur_1111_39335"
|
||||
/>
|
||||
</filter>
|
||||
<filter
|
||||
id="filter1_d_1111_39335"
|
||||
x="-15.8517"
|
||||
y="-10.8855"
|
||||
width="51.7033"
|
||||
height="53.2229"
|
||||
filterUnits="userSpaceOnUse"
|
||||
color-interpolation-filters="sRGB"
|
||||
>
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix
|
||||
in="SourceAlpha"
|
||||
type="matrix"
|
||||
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
||||
result="hardAlpha"
|
||||
/>
|
||||
<feOffset dy="5.72711" />
|
||||
<feGaussianBlur stdDeviation="10.7383" />
|
||||
<feColorMatrix
|
||||
type="matrix"
|
||||
values="0 0 0 0 0.0352941 0 0 0 0 0.0627451 0 0 0 0 0.109804 0 0 0 0.12 0"
|
||||
/>
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in2="BackgroundImageFix"
|
||||
result="effect1_dropShadow_1111_39335"
|
||||
/>
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="effect1_dropShadow_1111_39335"
|
||||
result="shape"
|
||||
/>
|
||||
</filter>
|
||||
<clipPath id="clip0_1111_39335">
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
<clipPath id="clip1_1111_39335">
|
||||
<rect width="20" height="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,12 +0,0 @@
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20 3.51238C19.2477 3.85565 18.452 4.08379 17.6375 4.18977C18.4958 3.65705 19.1376 2.8135 19.4412 1.81891C18.6375 2.31758 17.7582 2.66875 16.8412 2.85722C16.2791 2.22923 15.5493 1.79265 14.7469 1.60417C13.9444 1.4157 13.1063 1.48406 12.3415 1.80037C11.5766 2.11667 10.9204 2.66631 10.458 3.37786C9.99566 4.08942 9.74853 4.93 9.74875 5.79039C9.74875 6.13039 9.77625 6.45732 9.84375 6.76855C8.2126 6.68489 6.6167 6.242 5.16038 5.46885C3.70406 4.6957 2.42012 3.60969 1.3925 2.28183C0.866024 3.22581 0.703261 4.34422 0.937354 5.40933C1.17145 6.47445 1.78479 7.40617 2.6525 8.01479C2.00345 7.99658 1.36809 7.81539 0.8 7.48648V7.53356C0.801087 8.52405 1.12833 9.48391 1.72665 10.2516C2.32496 11.0192 3.15781 11.5478 4.085 11.7483C3.73423 11.845 3.37273 11.8925 3.01 11.8895C2.74949 11.8944 2.48923 11.8698 2.23375 11.8163C2.49868 12.6676 3.00936 13.4121 3.69549 13.9472C4.38162 14.4824 5.20945 14.7818 6.065 14.8044C4.61367 15.9918 2.82372 16.636 0.98125 16.6338C0.645 16.6338 0.3225 16.6182 0 16.575C1.87485 17.8389 4.05959 18.5075 6.29 18.4999C13.835 18.4999 17.96 11.9614 17.96 6.29386C17.96 6.10424 17.9538 5.92116 17.945 5.73939C18.7537 5.13391 19.4501 4.37923 20 3.51238Z"
|
||||
fill="#647084"
|
||||
/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,60 +0,0 @@
|
||||
import { Text } from '@status-im/components'
|
||||
import { ChevronRightIcon } from '@status-im/icons'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import { Link } from '../link'
|
||||
import { formatSegment } from './format-segment'
|
||||
|
||||
type Props = {
|
||||
cutFirstSegment?: boolean
|
||||
}
|
||||
|
||||
const Breadcrumbs = (props: Props) => {
|
||||
const { cutFirstSegment = true } = props
|
||||
|
||||
const router = useRouter()
|
||||
const { asPath } = router
|
||||
|
||||
// Splits the current path into segments
|
||||
const segments = asPath.split('/').filter(segment => segment !== '')
|
||||
|
||||
return (
|
||||
<nav className="flex items-center space-x-1">
|
||||
{segments.map((segment, index) => {
|
||||
// Builds the path up to the current segment
|
||||
const path = `/${segments.slice(0, index + 1).join('/')}`
|
||||
|
||||
// Determines if the current segment is the last one
|
||||
const isLastSegment = index === segments.length - 1
|
||||
|
||||
// If the first segment should be cut, skip it
|
||||
if (cutFirstSegment && index === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center" key={segment}>
|
||||
{/* Always render the first chevron unless cutFirstSegment is true and we need to avoid render the chevron before */}
|
||||
{!cutFirstSegment || (cutFirstSegment && index > 1) ? (
|
||||
<ChevronRightIcon size={20} color="$neutral-40" />
|
||||
) : null}
|
||||
|
||||
{isLastSegment ? (
|
||||
<Text size={15} color="$neutral-50" weight="medium">
|
||||
{formatSegment(segment)}
|
||||
</Text>
|
||||
) : (
|
||||
<Link href={path}>
|
||||
<Text size={15} weight="medium">
|
||||
{formatSegment(segment)}
|
||||
</Text>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export { Breadcrumbs }
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Util function that formats a segment for the breadcrumbs
|
||||
* @param segment - string to be formatted
|
||||
* formatSegment @returns a formatted string with the first letter of each word capitalized and spaces instead of "-"
|
||||
**/
|
||||
const formatSegment = (segment: string): string => {
|
||||
// Replaces "-" with a space and capitalize the first letter of each word
|
||||
const words = segment.split('-')
|
||||
const formattedSegment = words
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ')
|
||||
return formattedSegment
|
||||
}
|
||||
|
||||
export { formatSegment }
|
||||
@@ -1,161 +0,0 @@
|
||||
import { cva } from 'class-variance-authority'
|
||||
import Image from 'next/image'
|
||||
|
||||
import type { StaticImageData } from 'next/image'
|
||||
|
||||
// Variants for the grid hero class names
|
||||
const biggerCardClassNames = cva(
|
||||
[
|
||||
'min-w-[350px] rounded-[40px]',
|
||||
'px-[22px] py-6 sm:min-w-0 sm:px-[35px] sm:py-[48px] lg:px-5 xl:px-[34px] xl:py-[68px] 2xl:px-[73px]',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['bg-customisation-yellow/10'],
|
||||
turquoise: ['bg-customisation-turquoise/10'],
|
||||
purple: ['bg-customisation-purple/10'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const imagesWithBorders = cva(['border-4', 'rounded-3xl'], {
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['border-customisation-yellow/5'],
|
||||
turquoise: ['border-customisation-turquoise/5'],
|
||||
purple: ['border-customisation-purple/5'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const imagesWithBordersTopOrBottom = cva(['border-4', 'rounded-3xl'], {
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['border-customisation-yellow/5'],
|
||||
turquoise: ['border-customisation-turquoise/5'],
|
||||
purple: ['border-customisation-purple/5'],
|
||||
},
|
||||
alignment: {
|
||||
top: ['border-t-0'],
|
||||
bottom: ['border-b-0'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const thirdCardClassNames = cva(
|
||||
[
|
||||
'flex min-w-[calc(50%-10px)] rounded-[40px]',
|
||||
'px-[22px] sm:min-w-0 sm:px-[35px] lg:px-5 xl:px-[34px] 2xl:px-[73px]',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['bg-customisation-yellow/10'],
|
||||
turquoise: ['bg-customisation-turquoise/10'],
|
||||
purple: ['bg-customisation-purple/10'],
|
||||
},
|
||||
alignment: {
|
||||
top: [
|
||||
'pt-0',
|
||||
'pb-6 sm:pb-[48px] xl:pb-[68px]',
|
||||
'items-start',
|
||||
'justify-start',
|
||||
],
|
||||
bottom: [
|
||||
'pb-0',
|
||||
'pt-6 sm:pt-[48px] xl:pt-[68px]',
|
||||
'items-end',
|
||||
'justify-end',
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const fourthCardClassNames = cva(
|
||||
[
|
||||
'flex min-w-[calc(50%-10px)] rounded-[40px]',
|
||||
'grow items-center justify-center px-0 sm:px-5 md:px-10 lg:px-5 2xl:px-10',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['bg-customisation-yellow/10'],
|
||||
turquoise: ['bg-customisation-turquoise/10'],
|
||||
purple: ['bg-customisation-purple/10'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type Props = {
|
||||
color: 'yellow' | 'turquoise' | 'purple'
|
||||
cardOne: {
|
||||
image: StaticImageData
|
||||
alt: string
|
||||
}
|
||||
cardTwo: {
|
||||
image: StaticImageData
|
||||
alt: string
|
||||
}
|
||||
cardThree: {
|
||||
image: StaticImageData
|
||||
alt: string
|
||||
alignment?: 'top' | 'bottom'
|
||||
}
|
||||
cardFour: {
|
||||
image: StaticImageData
|
||||
alt: string
|
||||
}
|
||||
}
|
||||
|
||||
const GridHero = (props: Props) => {
|
||||
const { color, cardOne, cardTwo, cardThree, cardFour } = props
|
||||
|
||||
return (
|
||||
<div className="relative z-[2] flex w-full max-w-[1504px] justify-start overflow-x-auto px-5 sm:justify-center sm:px-10">
|
||||
<div className="flex min-w-[712px] flex-row gap-3 sm:min-w-fit sm:flex-col sm:gap-5 lg:flex-row">
|
||||
<div className="flex flex-row gap-3 sm:gap-5">
|
||||
<div className={biggerCardClassNames({ color })}>
|
||||
<Image
|
||||
src={cardOne.image}
|
||||
alt={cardOne.alt}
|
||||
className={imagesWithBorders({ color })}
|
||||
/>
|
||||
</div>
|
||||
<div className={biggerCardClassNames({ color })}>
|
||||
<Image
|
||||
src={cardTwo.image}
|
||||
alt={cardTwo.alt}
|
||||
className={imagesWithBorders({ color })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-w-[350px] flex-col gap-3 pr-5 sm:min-w-0 sm:flex-row sm:gap-5 sm:pr-0 lg:flex-col">
|
||||
<div
|
||||
className={thirdCardClassNames({
|
||||
color,
|
||||
alignment: cardThree.alignment || 'bottom',
|
||||
})}
|
||||
>
|
||||
<Image
|
||||
src={cardThree.image}
|
||||
alt={cardThree.alt}
|
||||
className={imagesWithBordersTopOrBottom({
|
||||
color,
|
||||
alignment: cardThree.alignment || 'bottom',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div className={fourthCardClassNames({ color })}>
|
||||
<Image src={cardFour.image} alt={cardFour.alt} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { GridHero }
|
||||
@@ -1,2 +0,0 @@
|
||||
export { GridHero } from './grid-hero'
|
||||
export { Section } from './section'
|
||||
@@ -1,149 +0,0 @@
|
||||
import { Text } from '@status-im/components'
|
||||
import { PopupIcon } from '@status-im/icons'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { Border } from './border'
|
||||
|
||||
import type { StaticImageData } from 'next/image'
|
||||
|
||||
type Props = {
|
||||
direction?: 'ltr' | 'rtl'
|
||||
title: string
|
||||
description: string
|
||||
image: StaticImageData
|
||||
imageAlt: string
|
||||
imageSecondary: StaticImageData
|
||||
imageSecondaryAlt: string
|
||||
secondaryDescription: string
|
||||
secondaryTitle: string
|
||||
customNode?: React.ReactNode
|
||||
color: 'yellow' | 'turquoise' | 'purple'
|
||||
information?: string
|
||||
}
|
||||
|
||||
const containerClassNames = cva(
|
||||
[
|
||||
'relative flex w-full justify-center',
|
||||
'overflow-hidden',
|
||||
'max-h-[854px]',
|
||||
'max-w-[582px]',
|
||||
'rounded-[32px]',
|
||||
'py-[65px]',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['bg-customisation-yellow/10'],
|
||||
turquoise: ['bg-customisation-turquoise/10'],
|
||||
purple: ['bg-customisation-purple/10'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const imageClassNames = cva(
|
||||
['rounded-3xl', 'border-4', 'h-auto max-h-[724px] w-auto'],
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['border-customisation-yellow/5'],
|
||||
turquoise: ['border-customisation-turquoise/5'],
|
||||
purple: ['border-customisation-purple/5'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const borderContainerClassNames = cva(
|
||||
['absolute left-0 top-0', 'w-full', 'h-[100%]'],
|
||||
{
|
||||
variants: {
|
||||
color: {
|
||||
yellow: ['text-customisation-yellow/5'],
|
||||
turquoise: ['text-customisation-turquoise/5'],
|
||||
purple: ['text-customisation-purple/5'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Section = (props: Props) => {
|
||||
const {
|
||||
title,
|
||||
color,
|
||||
description,
|
||||
image,
|
||||
imageAlt,
|
||||
imageSecondary,
|
||||
imageSecondaryAlt,
|
||||
secondaryDescription,
|
||||
secondaryTitle,
|
||||
direction = 'ltr',
|
||||
} = props
|
||||
|
||||
const directionOrder = direction === 'ltr' ? 'order-0' : 'order-1'
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<div className="relative z-[3] grid auto-cols-auto grid-flow-dense auto-rows-[1fr] grid-cols-1 gap-24 px-5 md:grid-cols-2 lg:gap-12 lg:px-[100px] xl:gap-[140px]">
|
||||
<div
|
||||
className={`${directionOrder} flex max-w-[1504px] justify-center overflow-hidden rounded-[32px]`}
|
||||
>
|
||||
<div className={containerClassNames({ color })}>
|
||||
<div className={borderContainerClassNames({ color })}>
|
||||
<Border />
|
||||
</div>
|
||||
<div className="absolute left-0 top-0 h-full w-full bg-[url('/assets/wallet/texture.png')] bg-contain bg-[left_top_0] bg-no-repeat" />
|
||||
<Image
|
||||
src={image}
|
||||
alt={imageAlt}
|
||||
className={imageClassNames({ color })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-start md:justify-center">
|
||||
<div className="flex flex-col">
|
||||
<Image
|
||||
src={imageSecondary}
|
||||
alt={imageSecondaryAlt}
|
||||
width={48}
|
||||
height={48}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col pt-4">
|
||||
<Text size={27} weight="semibold">
|
||||
{title}
|
||||
</Text>
|
||||
<div className="relative flex pt-1">
|
||||
<Text size={27}>{description}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 flex flex-col rounded-[20px] border border-dashed border-neutral-80/20 p-4">
|
||||
<Text size={19} weight="semibold">
|
||||
{secondaryTitle}
|
||||
</Text>
|
||||
<div className="flex pt-1">
|
||||
<Text size={19}>
|
||||
{secondaryDescription}
|
||||
{props.information && (
|
||||
<span className="relative left-1 top-1 inline-block">
|
||||
<PopupIcon size={20} color="$neutral-50" />
|
||||
</span>
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{props.customNode && (
|
||||
<div className="mt-4 flex flex-col">{props.customNode}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Section }
|
||||
@@ -1,47 +0,0 @@
|
||||
import { Stack } from '@tamagui/core'
|
||||
import { ParentSize } from '@visx/responsive'
|
||||
|
||||
import { ChartComponent, Empty, Loading } from './components'
|
||||
|
||||
type DayType = {
|
||||
date: string
|
||||
open_issues: number
|
||||
closed_issues: number
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: DayType[]
|
||||
height?: number
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const Chart = (props: Props) => {
|
||||
const { data, isLoading, ...rest } = props
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Stack width="100%" height={rest.height}>
|
||||
<Loading />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
return (
|
||||
<Stack width="100%" height={rest.height}>
|
||||
<Empty />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ParentSize style={{ maxHeight: 326 }}>
|
||||
{({ width }) => {
|
||||
return <ChartComponent {...rest} data={data} width={width} />
|
||||
}}
|
||||
</ParentSize>
|
||||
)
|
||||
}
|
||||
|
||||
export { Chart }
|
||||
export type { Props as ChartProps, DayType }
|
||||
@@ -1,98 +0,0 @@
|
||||
import { animated } from '@react-spring/web'
|
||||
import { curveMonotoneX } from '@visx/curve'
|
||||
import { LinearGradient } from '@visx/gradient'
|
||||
import { AreaClosed } from '@visx/shape'
|
||||
|
||||
import { colors } from './chart-component'
|
||||
|
||||
import type { SpringValue } from '@react-spring/web'
|
||||
import type { ScaleLinear, ScaleTime } from 'd3-scale'
|
||||
|
||||
type Datum = {
|
||||
date: Date
|
||||
value: number
|
||||
}
|
||||
|
||||
type Props = {
|
||||
yScale: ScaleLinear<number, number, never>
|
||||
xScale: ScaleTime<number, number, never>
|
||||
totalIssuesData: Datum[]
|
||||
closedIssuesData: Datum[]
|
||||
clipPathAnimation: {
|
||||
clipPath: SpringValue<string>
|
||||
}
|
||||
}
|
||||
|
||||
const AnimatedAreaClosed = animated(AreaClosed)
|
||||
|
||||
const Areas = (props: Props) => {
|
||||
const {
|
||||
clipPathAnimation,
|
||||
closedIssuesData,
|
||||
totalIssuesData,
|
||||
xScale,
|
||||
yScale,
|
||||
} = props
|
||||
return (
|
||||
<>
|
||||
<LinearGradient
|
||||
id="gradient-columns"
|
||||
from={'rgba(255, 255, 255, 1)'}
|
||||
to={'rgba(255, 255, 255, 0)'}
|
||||
fromOpacity={1}
|
||||
toOpacity={0}
|
||||
/>
|
||||
<LinearGradient
|
||||
id="gradient"
|
||||
from={colors.total}
|
||||
to={colors.background}
|
||||
fromOpacity={0.1}
|
||||
toOpacity={0}
|
||||
/>
|
||||
|
||||
<LinearGradient
|
||||
id="gradient-open"
|
||||
from={colors.closed}
|
||||
to={colors.background}
|
||||
fromOpacity={0.1}
|
||||
toOpacity={0}
|
||||
/>
|
||||
|
||||
{/* Total issues area */}
|
||||
<AnimatedAreaClosed
|
||||
data={totalIssuesData}
|
||||
x={d => {
|
||||
const datum = d as Datum
|
||||
return xScale(datum.date)
|
||||
}}
|
||||
y={d => {
|
||||
const datum = d as Datum
|
||||
return yScale(datum.value)
|
||||
}}
|
||||
yScale={yScale}
|
||||
fill="url(#gradient)"
|
||||
curve={curveMonotoneX}
|
||||
style={{ ...clipPathAnimation, zIndex: 10 }}
|
||||
/>
|
||||
|
||||
{/* Closed issues area */}
|
||||
<AnimatedAreaClosed
|
||||
data={closedIssuesData}
|
||||
x={d => {
|
||||
const datum = d as Datum
|
||||
return xScale(datum.date)
|
||||
}}
|
||||
y={d => {
|
||||
const datum = d as Datum
|
||||
return yScale(datum.value)
|
||||
}}
|
||||
yScale={yScale}
|
||||
fill="url(#gradient-open)"
|
||||
curve={curveMonotoneX}
|
||||
style={{ ...clipPathAnimation, zIndex: 10 }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { Areas }
|
||||
@@ -1,2 +0,0 @@
|
||||
export { LineA } from './line-a'
|
||||
export { LineB } from './line-b'
|
||||
@@ -1,15 +0,0 @@
|
||||
const LineA = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1066 104" fill="none">
|
||||
<path
|
||||
stroke="#F5F6F8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="m1 103 48.375-3a533.063 533.063 0 0 0 95.371-14.666v0a533.09 533.09 0 0 1 100.856-14.978L291.182 68l96.727-16.5 96.727-6.5 96.728-12 96.727-6 96.727-6 96.727-8h96.728L1065 1"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export { LineA }
|
||||
@@ -1,15 +0,0 @@
|
||||
const LineB = () => {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1066 154" fill="none">
|
||||
<path
|
||||
stroke="#F5F6F8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="m1 153 30.72-1.906a358.367 358.367 0 0 0 126.206-31.478v0a358.437 358.437 0 0 1 75.827-24.742l11.248-2.325A460.974 460.974 0 0 1 338.339 83h146.297l96.728-10v0a638.4 638.4 0 0 0 189.113-28.654L774.818 43l51.57-10.663A450.746 450.746 0 0 1 917.658 23v0c33.638 0 67.169-3.765 99.972-11.225L1065 1"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export { LineB }
|
||||
@@ -1,235 +0,0 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { animated } from '@react-spring/web'
|
||||
import { Axis } from '@visx/axis'
|
||||
import { GridColumns } from '@visx/grid'
|
||||
import { Group } from '@visx/group'
|
||||
import { scaleLinear, scaleTime } from '@visx/scale'
|
||||
import { extent, max } from 'd3-array'
|
||||
|
||||
import { getClosedIssues, getTotalIssues } from '../helpers/get-data'
|
||||
import { useAnimations, useChartTooltip } from '../hooks/'
|
||||
import { format } from '../utils/format-time'
|
||||
import { Areas } from './areas'
|
||||
import { ChartTooltip } from './chart-tooltip'
|
||||
import { Lines } from './lines'
|
||||
import { Markers } from './markers'
|
||||
|
||||
import type { DayType } from '../chart'
|
||||
|
||||
// defining colors
|
||||
export const colors = {
|
||||
total: '#E95460',
|
||||
closed: '#23ADA0',
|
||||
background: '#F0F2F5',
|
||||
marker: '#09101C',
|
||||
totalGradient: 'rgba(233, 84, 96, 0.3)',
|
||||
closedGradient: 'rgba(35, 173, 160, 0.3)',
|
||||
white: '#FFF',
|
||||
} as const
|
||||
|
||||
interface Props {
|
||||
data: DayType[]
|
||||
height?: number
|
||||
width?: number
|
||||
}
|
||||
|
||||
const AnimatedGridColumns = animated(GridColumns)
|
||||
|
||||
const ChartComponent = (props: Props): JSX.Element => {
|
||||
const { data, width: defaultWidth, height: defaultHeight } = props
|
||||
|
||||
// Extract dates, open issues, and closed issues from data
|
||||
const dates = data.map(d => new Date(d.date))
|
||||
|
||||
// Define dimensions
|
||||
const height = defaultHeight || 300
|
||||
const width = defaultWidth || 300
|
||||
const margin = { top: 20, right: 0, bottom: 30, left: 26 }
|
||||
const innerWidth = (width || 0) - margin.left - margin.right
|
||||
const innerHeight = height - margin.top - margin.bottom
|
||||
|
||||
const filteredDates = dates.filter(Boolean) // filters out undefined values
|
||||
// Calculate total issues by summing open and closed issues
|
||||
const totalIssues = data.map(d => d.open_issues + d.closed_issues)
|
||||
|
||||
const xDomain = extent(filteredDates) as [Date, Date]
|
||||
const xScale = scaleTime({
|
||||
domain: xDomain,
|
||||
range: [0, innerWidth],
|
||||
})
|
||||
|
||||
const numTicksBottom = Math.min(data.length, 12)
|
||||
|
||||
const yScale = scaleLinear({
|
||||
domain: [0, max(totalIssues) || 0],
|
||||
range: [innerHeight, 0],
|
||||
nice: true,
|
||||
})
|
||||
|
||||
const { tooltipData, updateTooltip, hideTooltip, tooltipOpen } =
|
||||
useChartTooltip({
|
||||
data,
|
||||
margin,
|
||||
dates,
|
||||
innerWidth,
|
||||
})
|
||||
|
||||
const {
|
||||
circleSpringClosed,
|
||||
circleSpringTotal,
|
||||
clipPathAnimation,
|
||||
drawingGridColumns,
|
||||
drawingLineStyle,
|
||||
opacityAnimation,
|
||||
tooltipAnimation,
|
||||
} = useAnimations({
|
||||
data,
|
||||
margin,
|
||||
innerHeight,
|
||||
tooltipData,
|
||||
yScale,
|
||||
xScale,
|
||||
})
|
||||
|
||||
// Convert data to array of objects with `date` and `value` properties
|
||||
const totalIssuesData = useMemo(
|
||||
() =>
|
||||
data.map(d => ({
|
||||
date: new Date(d.date),
|
||||
value: getTotalIssues(d),
|
||||
})),
|
||||
[data]
|
||||
)
|
||||
|
||||
const closedIssuesData = useMemo(
|
||||
() =>
|
||||
data.map(d => ({
|
||||
date: new Date(d.date),
|
||||
value: getClosedIssues(d),
|
||||
})),
|
||||
[data]
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<svg width={width} height={height} overflow="visible">
|
||||
<Group left={margin.left} top={margin.top}>
|
||||
{/* x-axis */}
|
||||
<Axis
|
||||
top={innerHeight}
|
||||
scale={xScale}
|
||||
hideTicks
|
||||
hideAxisLine
|
||||
hideZero
|
||||
numTicks={numTicksBottom}
|
||||
tickLabelProps={(_, index) => {
|
||||
const textAnchor =
|
||||
index === 0
|
||||
? 'start'
|
||||
: index === data.length - 1
|
||||
? 'end'
|
||||
: 'middle'
|
||||
return {
|
||||
dy: '.33em',
|
||||
fill: '#A1ABBD',
|
||||
fontFamily: 'Menlo',
|
||||
fontSize: 11,
|
||||
textAnchor,
|
||||
}
|
||||
}}
|
||||
tickFormat={value => {
|
||||
if (typeof value === 'number') {
|
||||
return value.toString() // Handle number values
|
||||
} else if (value instanceof Date) {
|
||||
// Specify the desired date format
|
||||
return format(value) // Handle date values
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* y-axis */}
|
||||
<Axis
|
||||
orientation="left"
|
||||
scale={yScale}
|
||||
hideTicks
|
||||
hideAxisLine
|
||||
numTicks={6}
|
||||
tickComponent={({ formattedValue, ...tickProps }) => (
|
||||
<text
|
||||
{...tickProps}
|
||||
fill="#A1ABBD"
|
||||
fontSize={11}
|
||||
fontFamily="Menlo"
|
||||
textAnchor="middle"
|
||||
dx="-1em"
|
||||
>
|
||||
{formattedValue}
|
||||
</text>
|
||||
)}
|
||||
/>
|
||||
|
||||
<AnimatedGridColumns
|
||||
scale={xScale}
|
||||
height={innerHeight}
|
||||
style={drawingGridColumns}
|
||||
numTicks={numTicksBottom}
|
||||
/>
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={innerWidth}
|
||||
height={innerHeight}
|
||||
fill="url(#gradient-columns)"
|
||||
/>
|
||||
|
||||
<Lines
|
||||
closedIssuesData={closedIssuesData}
|
||||
drawingLineStyle={drawingLineStyle}
|
||||
totalIssuesData={totalIssuesData}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
/>
|
||||
|
||||
<Areas
|
||||
clipPathAnimation={clipPathAnimation}
|
||||
closedIssuesData={closedIssuesData}
|
||||
totalIssuesData={totalIssuesData}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
/>
|
||||
{tooltipOpen && (
|
||||
<Markers
|
||||
circleSpringClosed={circleSpringClosed}
|
||||
circleSpringTotal={circleSpringTotal}
|
||||
innerHeight={innerHeight}
|
||||
opacityAnimation={opacityAnimation}
|
||||
/>
|
||||
)}
|
||||
<rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={innerWidth}
|
||||
height={innerHeight}
|
||||
onTouchStart={updateTooltip}
|
||||
fill="transparent"
|
||||
onTouchMove={updateTooltip}
|
||||
onMouseMove={updateTooltip}
|
||||
onMouseLeave={() => hideTooltip()}
|
||||
/>
|
||||
</Group>
|
||||
</svg>
|
||||
{/* render a tooltip */}
|
||||
{tooltipOpen && (
|
||||
<ChartTooltip
|
||||
tooltipAnimation={tooltipAnimation}
|
||||
opacityAnimation={opacityAnimation}
|
||||
tooltipData={tooltipData}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { ChartComponent }
|
||||
@@ -1,115 +0,0 @@
|
||||
import { animated } from '@react-spring/web'
|
||||
import { Text } from '@status-im/components'
|
||||
import { DoneIcon, OpenIcon } from '@status-im/icons'
|
||||
import { Stack } from '@tamagui/core'
|
||||
import { defaultStyles, TooltipWithBounds } from '@visx/tooltip'
|
||||
|
||||
import type { TooltipData } from '../hooks/use-chart-tooltip'
|
||||
import type { SpringValue } from '@react-spring/web'
|
||||
|
||||
const AnimatedTooltip = animated(TooltipWithBounds)
|
||||
|
||||
type Props = {
|
||||
tooltipData: TooltipData
|
||||
opacityAnimation: {
|
||||
opacity: SpringValue<number>
|
||||
}
|
||||
tooltipAnimation: {
|
||||
x: SpringValue<number>
|
||||
y: SpringValue<number>
|
||||
}
|
||||
}
|
||||
|
||||
// defining tooltip styles
|
||||
const tooltipStyles: React.CSSProperties = {
|
||||
...defaultStyles,
|
||||
minWidth: 272,
|
||||
padding: 12,
|
||||
backgroundColor: '#FFF',
|
||||
border: '1px solid #F0F2F5',
|
||||
boxShadow: '0px 2px 20px rgba(9, 16, 28, 0.04)',
|
||||
borderRadius: 20,
|
||||
marginLeft: 25,
|
||||
}
|
||||
|
||||
const ChartTooltip = (props: Props) => {
|
||||
const { tooltipData, opacityAnimation, tooltipAnimation } = props
|
||||
return (
|
||||
<AnimatedTooltip
|
||||
top={tooltipAnimation.y}
|
||||
left={tooltipAnimation.x}
|
||||
style={{ ...tooltipStyles, opacity: opacityAnimation.opacity }}
|
||||
className="rounded-2xl"
|
||||
>
|
||||
<Stack flexDirection="row" alignItems="center">
|
||||
<Text size={19} weight="semibold">
|
||||
{tooltipData.totalIssues}
|
||||
</Text>
|
||||
<Stack ml={3} alignItems="center">
|
||||
<Text size={15} weight="medium">
|
||||
issues
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack pb={12}>
|
||||
<Text size={13} weight="medium" color="$neutral-50">
|
||||
{tooltipData.formattedDate}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack borderRadius="$8" backgroundColor="$danger-50-opa-10">
|
||||
<Stack
|
||||
animation="slow"
|
||||
height={8}
|
||||
backgroundColor="$success-50"
|
||||
width={`${tooltipData.completedIssuesPercentage}%`}
|
||||
borderRadius="$8"
|
||||
/>
|
||||
</Stack>
|
||||
<Stack flexDirection="row" alignItems="center" pt={18}>
|
||||
<OpenIcon size={16} color="$neutral-40" />
|
||||
<Stack px={4}>
|
||||
<Text size={13} weight="medium">
|
||||
{tooltipData.openIssues} open
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
backgroundColor="$danger-50-opa-30"
|
||||
borderRadius="$20"
|
||||
px={6}
|
||||
py={2}
|
||||
minWidth={36}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text size={11} weight="medium" color="$danger-50">
|
||||
{`${tooltipData.openIssuesPercentage}%`}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack flexDirection="row" alignItems="center" pt={8}>
|
||||
<DoneIcon size={16} color="$neutral-40" />
|
||||
<Stack px={4}>
|
||||
<Text size={13} weight="medium">
|
||||
{tooltipData.closedIssues} closes
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
minWidth={36}
|
||||
backgroundColor="$success-50-opa-30"
|
||||
borderRadius="$20"
|
||||
px={6}
|
||||
py={2}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text size={11} weight="medium" color="$success-50">
|
||||
{`${tooltipData.completedIssuesPercentage}%`}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</AnimatedTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export { ChartTooltip }
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Image, Text } from '@status-im/components'
|
||||
|
||||
import { LineA, LineB } from './assets'
|
||||
|
||||
const Empty = () => {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||
<div className="relative flex h-full w-full items-center justify-center">
|
||||
<div className="relative z-10 flex flex-col items-center justify-center">
|
||||
<Image src={'/assets/chart/empty.png'} width={80} height={80} />
|
||||
<div className="pb-3" />
|
||||
<Text size={15} weight="semibold">
|
||||
No results found
|
||||
</Text>
|
||||
<div className="pb-1" />
|
||||
<Text size={13} color="$neutral-50">
|
||||
Try adjusting your search or filter to find what you’re looking for.
|
||||
</Text>
|
||||
</div>
|
||||
<div className="left-50 absolute top-16 w-full opacity-60">
|
||||
<LineA />
|
||||
</div>
|
||||
<div className="absolute left-0 top-28 w-full opacity-60">
|
||||
<LineB />
|
||||
</div>
|
||||
<div
|
||||
className="absolute left-0 top-0 h-24 w-full"
|
||||
style={{
|
||||
background: `linear-gradient(180deg, white, transparent)`,
|
||||
}}
|
||||
/>
|
||||
<div className="absolute flex h-full w-full justify-between opacity-80">
|
||||
{Array.from({ length: 12 }).map((_, i) => (
|
||||
<div
|
||||
className="h-full w-1"
|
||||
style={{
|
||||
borderLeft: '1px dashed #F0F2F5',
|
||||
}}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Empty }
|
||||
@@ -1,3 +0,0 @@
|
||||
export { ChartComponent } from './chart-component'
|
||||
export { Empty } from './empty'
|
||||
export { Loading } from './loading'
|
||||
@@ -1,73 +0,0 @@
|
||||
import { animated } from '@react-spring/web'
|
||||
import { curveMonotoneX } from '@visx/curve'
|
||||
import { LinePath } from '@visx/shape'
|
||||
|
||||
import { colors } from './chart-component'
|
||||
|
||||
import type { SpringValue } from '@react-spring/web'
|
||||
import type { ScaleLinear, ScaleTime } from 'd3-scale'
|
||||
|
||||
type Datum = {
|
||||
date: Date
|
||||
value: number
|
||||
}
|
||||
|
||||
type Props = {
|
||||
yScale: ScaleLinear<number, number, never>
|
||||
xScale: ScaleTime<number, number, never>
|
||||
totalIssuesData: Datum[]
|
||||
closedIssuesData: Datum[]
|
||||
drawingLineStyle: {
|
||||
strokeDasharray: SpringValue<string>
|
||||
}
|
||||
}
|
||||
|
||||
const AnimatedLinePath = animated(LinePath)
|
||||
|
||||
const Lines = (props: Props) => {
|
||||
const {
|
||||
closedIssuesData,
|
||||
drawingLineStyle,
|
||||
xScale,
|
||||
yScale,
|
||||
totalIssuesData,
|
||||
} = props
|
||||
return (
|
||||
<>
|
||||
{/* Total issues line */}
|
||||
<AnimatedLinePath
|
||||
data={totalIssuesData}
|
||||
x={d => {
|
||||
const datum = d as Datum
|
||||
return xScale(datum.date)
|
||||
}}
|
||||
y={d => {
|
||||
const datum = d as Datum
|
||||
return yScale(datum.value)
|
||||
}}
|
||||
stroke={colors.total}
|
||||
strokeWidth={2}
|
||||
curve={curveMonotoneX}
|
||||
style={drawingLineStyle}
|
||||
/>
|
||||
|
||||
{/* Closed issues line */}
|
||||
<AnimatedLinePath
|
||||
data={closedIssuesData}
|
||||
x={d => {
|
||||
const datum = d as Datum
|
||||
return xScale(datum.date)
|
||||
}}
|
||||
y={d => {
|
||||
const datum = d as Datum
|
||||
return yScale(datum.value)
|
||||
}}
|
||||
stroke={colors.closed}
|
||||
strokeWidth={2}
|
||||
curve={curveMonotoneX}
|
||||
style={drawingLineStyle}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export { Lines }
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Skeleton } from '@status-im/components'
|
||||
|
||||
const Loading = () => {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||
<div className="relative flex h-full w-full">
|
||||
<div
|
||||
className="absolute left-0 top-0 h-24 w-full"
|
||||
style={{
|
||||
background: `linear-gradient(180deg, white, transparent)`,
|
||||
}}
|
||||
/>
|
||||
<div className="flex h-full w-10 flex-col justify-between ">
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<Skeleton key={i} width={20} height={12} />
|
||||
))}
|
||||
</div>
|
||||
<div className="flex h-full w-full justify-between pb-4">
|
||||
{Array.from({ length: 12 }).map((_, i) => (
|
||||
<div
|
||||
className="h-full w-1"
|
||||
style={{
|
||||
borderLeft: '1px dashed #F0F2F5',
|
||||
}}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full flex-row justify-between pl-9">
|
||||
{Array.from({ length: 12 }).map((_, i) => (
|
||||
<Skeleton key={i} width={32} height={12} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Loading }
|
||||
@@ -1,74 +0,0 @@
|
||||
import { animated } from '@react-spring/web'
|
||||
import { GlyphCircle } from '@visx/glyph'
|
||||
import { Group } from '@visx/group'
|
||||
import { Line } from '@visx/shape'
|
||||
|
||||
import { colors } from './chart-component'
|
||||
|
||||
import type { SpringValue } from '@react-spring/web'
|
||||
|
||||
type Props = {
|
||||
innerHeight: number
|
||||
circleSpringTotal: {
|
||||
x: SpringValue<number>
|
||||
y: SpringValue<number>
|
||||
}
|
||||
opacityAnimation: {
|
||||
opacity: SpringValue<number>
|
||||
}
|
||||
circleSpringClosed: {
|
||||
x: SpringValue<number>
|
||||
y: SpringValue<number>
|
||||
}
|
||||
}
|
||||
|
||||
const AnimatedCircle = animated(GlyphCircle)
|
||||
const AnimatedLine = animated(Line)
|
||||
const AnimatedGroup = animated(Group)
|
||||
|
||||
const Markers = (props: Props) => {
|
||||
const {
|
||||
innerHeight,
|
||||
circleSpringTotal,
|
||||
opacityAnimation,
|
||||
circleSpringClosed,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatedGroup
|
||||
top={0}
|
||||
left={circleSpringTotal.x}
|
||||
opacity={opacityAnimation.opacity}
|
||||
>
|
||||
<AnimatedLine
|
||||
from={{ x: 0, y: innerHeight }}
|
||||
to={{ x: 0, y: 0 }}
|
||||
stroke={colors.marker}
|
||||
strokeWidth={1}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
</AnimatedGroup>
|
||||
<AnimatedCircle
|
||||
size={90}
|
||||
top={circleSpringTotal.y}
|
||||
left={circleSpringTotal.x}
|
||||
strokeWidth={1}
|
||||
stroke={colors.background}
|
||||
fill={colors.marker}
|
||||
opacity={opacityAnimation.opacity}
|
||||
/>
|
||||
<AnimatedCircle
|
||||
size={90}
|
||||
top={circleSpringClosed.y}
|
||||
left={circleSpringClosed.x}
|
||||
strokeWidth={1}
|
||||
stroke={colors.background}
|
||||
fill={colors.marker}
|
||||
opacity={opacityAnimation.opacity}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export { Markers }
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { DayType } from '../chart'
|
||||
|
||||
/**
|
||||
* Gets the total issues of a day
|
||||
* @param d - the day to get the total issues
|
||||
* @returns the total issues of a day
|
||||
**/
|
||||
export const getTotalIssues = (d: DayType) => d?.open_issues + d?.closed_issues
|
||||
|
||||
/**
|
||||
* Gets the closed issues of a day
|
||||
* @param d - the day to get the closes issues
|
||||
* @returns the total issues of a day
|
||||
**/
|
||||
export const getClosedIssues = (d: DayType) => d?.closed_issues
|
||||
@@ -1,2 +0,0 @@
|
||||
export { useAnimations } from './use-animations'
|
||||
export { useChartTooltip } from './use-chart-tooltip'
|
||||
@@ -1,99 +0,0 @@
|
||||
import { config, useSpring } from '@react-spring/web'
|
||||
|
||||
import type { DayType } from '../chart'
|
||||
import type { TooltipData } from './use-chart-tooltip'
|
||||
import type { ScaleLinear, ScaleTime } from 'd3-scale'
|
||||
|
||||
type Props = {
|
||||
data: DayType[]
|
||||
yScale: ScaleLinear<number, number, never>
|
||||
xScale: ScaleTime<number, number, never>
|
||||
margin: {
|
||||
top: number
|
||||
right: number
|
||||
bottom: number
|
||||
left: number
|
||||
}
|
||||
innerHeight: number
|
||||
tooltipData: TooltipData
|
||||
}
|
||||
|
||||
/**
|
||||
* An custom hook that handles animations logic
|
||||
* @param data - the data to be used in the chart
|
||||
* @param margin - the margin of the chart
|
||||
* @param dates - the dates of the chart
|
||||
* @param innerHeight - the inner height of the chart
|
||||
* @param xScale - the xScale of the chart
|
||||
* @param yScale - the yScale of the chart
|
||||
* @param tooltipData - the data to be used in the tooltip
|
||||
|
||||
|
||||
* @returns circleSpringTotal - the spring animation for the total issues circle
|
||||
* @returns circleSpringClosed - the spring animation for the closed issues circle
|
||||
* @returns opacityAnimation - the spring animation for the some chart elements opacity
|
||||
* @returns tooltipAnimation - the spring animation for the tooltip
|
||||
* @returns drawingLineStyle - the spring animation for the drawing line style
|
||||
* @returns drawingGridColumns - the spring animation for the drawing grid columns
|
||||
* @returns clipPathAnimation - the spring animation for the clip path
|
||||
**/
|
||||
|
||||
const useAnimations = (props: Props) => {
|
||||
const { data, margin, innerHeight, tooltipData, xScale, yScale } = props
|
||||
|
||||
// Define spring for circle position
|
||||
const circleSpringTotal = useSpring({
|
||||
x: xScale(new Date(tooltipData?.date || '')),
|
||||
y: yScale(tooltipData?.totalIssues),
|
||||
config: config.gentle,
|
||||
})
|
||||
|
||||
const circleSpringClosed = useSpring({
|
||||
x: xScale(new Date(tooltipData?.date)),
|
||||
y: yScale(tooltipData?.closedIssues),
|
||||
config: config.gentle,
|
||||
})
|
||||
|
||||
const opacityAnimation = useSpring({
|
||||
opacity: data ? 1 : 0,
|
||||
config: config.gentle,
|
||||
delay: 200,
|
||||
})
|
||||
|
||||
const tooltipAnimation = useSpring({
|
||||
x: xScale(new Date(tooltipData?.date)),
|
||||
y: innerHeight + margin.top + margin.bottom,
|
||||
config: config.gentle,
|
||||
})
|
||||
|
||||
const [drawingLineStyle] = useSpring(() => ({
|
||||
from: { strokeDasharray: `${3000}, ${0}` },
|
||||
to: { strokeDasharray: `${0}, ${3000}` },
|
||||
reverse: true,
|
||||
config: { duration: 2000 },
|
||||
}))
|
||||
|
||||
const [drawingGridColumns] = useSpring(() => ({
|
||||
from: { strokeDasharray: `0, 150` },
|
||||
to: { strokeDasharray: '4,2' },
|
||||
config: { duration: 250 },
|
||||
}))
|
||||
|
||||
const [clipPathAnimation] = useSpring(() => ({
|
||||
from: { clipPath: 'inset(0 100% 0 0)' },
|
||||
to: { clipPath: 'inset(0 0 0 0)' },
|
||||
config: { duration: 800 },
|
||||
}))
|
||||
|
||||
return {
|
||||
circleSpringTotal,
|
||||
circleSpringClosed,
|
||||
opacityAnimation,
|
||||
tooltipAnimation,
|
||||
drawingLineStyle,
|
||||
drawingGridColumns,
|
||||
clipPathAnimation,
|
||||
}
|
||||
}
|
||||
|
||||
export { useAnimations }
|
||||
@@ -1,129 +0,0 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { localPoint } from '@visx/event'
|
||||
import { scaleTime } from '@visx/scale'
|
||||
import { useTooltip } from '@visx/tooltip'
|
||||
import { bisector, extent } from 'd3-array'
|
||||
|
||||
import { getClosedIssues, getTotalIssues } from '../helpers/get-data'
|
||||
import { formatDate } from '../utils/format-time'
|
||||
import { getPercentage } from '../utils/get-percentage'
|
||||
|
||||
import type { DayType } from '../chart'
|
||||
import type { EventType } from '@visx/event/lib/types'
|
||||
|
||||
type Props = {
|
||||
data: DayType[]
|
||||
dates: Date[]
|
||||
innerWidth: number
|
||||
margin: {
|
||||
top: number
|
||||
right: number
|
||||
bottom: number
|
||||
left: number
|
||||
}
|
||||
}
|
||||
|
||||
type TooltipData = {
|
||||
date: string
|
||||
completedIssuesPercentage: number
|
||||
openIssuesPercentage: number
|
||||
totalIssues: number
|
||||
openIssues: number
|
||||
closedIssues: number
|
||||
formattedDate: string
|
||||
}
|
||||
|
||||
/**
|
||||
* An custom hook that handles the tooltip logic
|
||||
* @param data - the data to be used in the chart
|
||||
* @param margin - the margin of the chart
|
||||
* @param dates - the dates of the chart
|
||||
* @param innerWidth - the inner width of the chart
|
||||
|
||||
* @returns tooltipData - the data to be used in the tooltip
|
||||
* @returns updateTooltip - a function that updates the tooltip
|
||||
* @returns hideTooltip - a function that hides the tooltip
|
||||
* @returns tooltipOpen - a boolean that indicates if the tooltip is open
|
||||
**/
|
||||
|
||||
const useChartTooltip = (props: Props) => {
|
||||
const { data, margin, dates, innerWidth } = props
|
||||
|
||||
// tooltip parameters
|
||||
const { tooltipData, showTooltip, hideTooltip, tooltipOpen } =
|
||||
useTooltip<TooltipData>({
|
||||
tooltipData: {
|
||||
date: '',
|
||||
completedIssuesPercentage: 0,
|
||||
openIssuesPercentage: 0,
|
||||
totalIssues: 0,
|
||||
openIssues: 0,
|
||||
closedIssues: 0,
|
||||
formattedDate: '',
|
||||
},
|
||||
})
|
||||
|
||||
const filteredDates = dates.filter(Boolean) // filters out undefined values
|
||||
|
||||
const getDate = useCallback((d: DayType) => new Date(d?.date), [])
|
||||
const bisectDate = bisector((d: DayType) => new Date(d?.date)).left
|
||||
|
||||
const xDomain = extent(filteredDates) as [Date, Date]
|
||||
const xScale = scaleTime({
|
||||
domain: xDomain,
|
||||
range: [0, innerWidth],
|
||||
})
|
||||
|
||||
// Define tooltip handler
|
||||
const updateTooltip = useCallback(
|
||||
(event: EventType) => {
|
||||
const { x } = localPoint(event) || { x: 0 }
|
||||
const x0 = xScale.invert(x - margin.left)
|
||||
|
||||
const index = bisectDate(data, x0, 1)
|
||||
const d0 = data[index - 1]
|
||||
const d1 = data[index]
|
||||
let d = d0
|
||||
|
||||
if (d1 && getDate(d1)) {
|
||||
d =
|
||||
x0.valueOf() - getDate(d0).valueOf() >
|
||||
getDate(d1).valueOf() - x0.valueOf()
|
||||
? d1
|
||||
: d0
|
||||
}
|
||||
|
||||
const closedIssues = getClosedIssues(d)
|
||||
const totalIssues = getTotalIssues(d)
|
||||
const openIssues = totalIssues - closedIssues
|
||||
|
||||
const completedIssuesPercentage = getPercentage(closedIssues, totalIssues)
|
||||
|
||||
const openIssuesPercentage = getPercentage(openIssues, totalIssues)
|
||||
|
||||
showTooltip({
|
||||
tooltipData: {
|
||||
date: d?.date,
|
||||
formattedDate: formatDate(getDate(d)),
|
||||
completedIssuesPercentage,
|
||||
openIssuesPercentage,
|
||||
closedIssues,
|
||||
totalIssues,
|
||||
openIssues,
|
||||
},
|
||||
})
|
||||
},
|
||||
[xScale, margin.left, bisectDate, data, getDate, showTooltip]
|
||||
)
|
||||
|
||||
return {
|
||||
tooltipData: tooltipData!,
|
||||
hideTooltip,
|
||||
updateTooltip,
|
||||
tooltipOpen,
|
||||
}
|
||||
}
|
||||
|
||||
export { useChartTooltip }
|
||||
export type { TooltipData, Props as UseChartTooltipProps }
|
||||
@@ -1,13 +0,0 @@
|
||||
import { timeFormat } from 'd3-time-format'
|
||||
|
||||
/**
|
||||
* Util functions that formats a date
|
||||
* formatDate @returns a string with the format 'MMM dd yyyy' - Ex: Jan 01 2021
|
||||
**/
|
||||
export const formatDate = timeFormat('%b %d %Y')
|
||||
|
||||
/**
|
||||
* Util functions that formats a date
|
||||
* format @returns a string with the format 'dd MMM' - Ex: 01 Jan
|
||||
**/
|
||||
export const format = timeFormat('%d %b')
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* An util function that gets the percentage of a value from a total
|
||||
* @param value - the value to calculate the percentage
|
||||
* @param total - the total value
|
||||
**/
|
||||
|
||||
export const getPercentage = (value: number, total: number): number =>
|
||||
Math.round((value / total) * 100)
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Calendar } from '@status-im/components/src/calendar/calendar'
|
||||
import { Popover } from '@status-im/components/src/popover'
|
||||
import { EditIcon } from '@status-im/icons'
|
||||
|
||||
import { formatDate } from '../chart/utils/format-time'
|
||||
|
||||
import type { DateRange } from '@status-im/components/src/calendar/calendar'
|
||||
|
||||
type Props = {
|
||||
selected?: DateRange
|
||||
onSelect: (selected?: DateRange) => void
|
||||
}
|
||||
|
||||
const DatePicker = (props: Props) => {
|
||||
const { selected, onSelect } = props
|
||||
|
||||
return (
|
||||
<div className="sticky bottom-5 flex justify-center">
|
||||
<Popover alignOffset={8} align="center" sideOffset={8}>
|
||||
<button className="active inline-flex min-h-[30px] cursor-pointer items-center justify-center gap-2 rounded-xl border-solid border-neutral-80/5 bg-blur-neutral-5/70 pl-3 pr-2 uppercase text-neutral-100 backdrop-blur-sm">
|
||||
<span className="text-[13px] font-medium text-blur-neutral-80/80">
|
||||
Filter between
|
||||
</span>
|
||||
<span className="text-[13px] font-medium text-neutral-100">
|
||||
{`${selected?.from ? formatDate(selected.from) : 'Start Date'} → ${
|
||||
selected?.to ? formatDate(selected.to) : 'End Date'
|
||||
}`}
|
||||
</span>
|
||||
<div className="h-full w-[1px] bg-neutral-80/5" />
|
||||
<EditIcon size={20} color="$neutral-80-opa-40" />
|
||||
</button>
|
||||
<Popover.Content>
|
||||
<Calendar
|
||||
mode="range"
|
||||
selected={selected}
|
||||
onSelect={onSelect}
|
||||
fixedWeeks
|
||||
/>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { DatePicker }
|
||||
@@ -1,112 +0,0 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { Tag, Text } from '@status-im/components'
|
||||
import { OpenIcon } from '@status-im/icons'
|
||||
|
||||
import { Chart } from './chart/chart'
|
||||
|
||||
const DATA = [
|
||||
{
|
||||
date: '2022-01-25',
|
||||
open_issues: 100,
|
||||
closed_issues: 2,
|
||||
},
|
||||
{
|
||||
date: '2022-01-26',
|
||||
open_issues: 100,
|
||||
closed_issues: 10,
|
||||
},
|
||||
{
|
||||
date: '2022-01-27',
|
||||
open_issues: 100,
|
||||
closed_issues: 20,
|
||||
},
|
||||
{
|
||||
date: '2022-01-28',
|
||||
open_issues: 90,
|
||||
closed_issues: 30,
|
||||
},
|
||||
{
|
||||
date: '2022-01-29',
|
||||
open_issues: 80,
|
||||
closed_issues: 40,
|
||||
},
|
||||
{
|
||||
date: '2022-01-30',
|
||||
open_issues: 40,
|
||||
closed_issues: 80,
|
||||
},
|
||||
{
|
||||
date: '2022-01-31',
|
||||
open_issues: 30,
|
||||
closed_issues: 90,
|
||||
},
|
||||
{
|
||||
date: '2022-02-01',
|
||||
open_issues: 25,
|
||||
closed_issues: 95,
|
||||
},
|
||||
{
|
||||
date: '2022-02-02',
|
||||
open_issues: 20,
|
||||
closed_issues: 98,
|
||||
},
|
||||
{
|
||||
date: '2022-02-03',
|
||||
open_issues: 10,
|
||||
closed_issues: 130,
|
||||
},
|
||||
]
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
description: string
|
||||
fullscreen?: boolean
|
||||
}
|
||||
|
||||
export const EpicOverview = (props: Props) => {
|
||||
const { title, description, fullscreen } = props
|
||||
|
||||
// Simulating loading state
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
useEffect(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
setIsLoading(false)
|
||||
}, 2000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text size={fullscreen ? 27 : 19} weight="semibold">
|
||||
{title}
|
||||
</Text>
|
||||
<OpenIcon size={20} />
|
||||
</div>
|
||||
<Text size={fullscreen ? 19 : 15} color="$neutral-50">
|
||||
{description}
|
||||
</Text>
|
||||
<div className="flex py-3">
|
||||
<Tag size={24} label="E:CommunitiesProtocol" color="$blue-50" />
|
||||
</div>
|
||||
|
||||
<Chart data={DATA} height={300} isLoading={isLoading} />
|
||||
|
||||
<div className="flex justify-between pt-3">
|
||||
<div className="flex gap-1">
|
||||
<Tag size={24} label="Communities" color="#FF7D46" icon="🧙♂️" />
|
||||
<Tag size={24} label="Wallet" color="#7140FD" icon="🎎" />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1">
|
||||
<Tag size={24} label="M:0.11.0" color="$danger-50" />
|
||||
<Tag size={24} label="M:0.12.0" color="$success-50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { Text } from '@status-im/components'
|
||||
|
||||
import { ERROR_CODES } from '@/consts/error-codes'
|
||||
|
||||
type Props = {
|
||||
errorCode: (typeof ERROR_CODES)[keyof typeof ERROR_CODES]
|
||||
}
|
||||
|
||||
export const ErrorPage = (props: Props) => {
|
||||
const { errorCode } = props
|
||||
|
||||
switch (errorCode) {
|
||||
// todo!: design review, not in designs
|
||||
case ERROR_CODES.NOT_FOUND:
|
||||
return (
|
||||
<div className="bg-white flex h-full w-full flex-col items-center justify-center gap-8 text-center">
|
||||
<div className="h-[160px] w-[160px] rounded-full bg-[#b3b3b3]" />
|
||||
<Text size={27} weight="semibold">
|
||||
Page not found.
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
|
||||
case ERROR_CODES.INTERNAL_SERVER_ERROR:
|
||||
default:
|
||||
return (
|
||||
<div className="bg-white flex h-full w-full flex-col items-center justify-center gap-8 text-center">
|
||||
<div className="h-[160px] w-[160px] rounded-full bg-[hsla(355,47%,50%,1)]" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<Text size={27} weight="semibold">
|
||||
{"Oh no, something's wrong!"}
|
||||
</Text>
|
||||
<Text size={19} weight="regular">
|
||||
Try reloading the page or come back later!
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Button, Text } from '@status-im/components'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
description: string
|
||||
action: string
|
||||
}
|
||||
|
||||
const ActionCard = (props: Props) => {
|
||||
const { title, description, action } = props
|
||||
|
||||
return (
|
||||
<div className="flex items-center rounded-[20px] border border-neutral-90 bg-neutral-95 px-5 py-3">
|
||||
<div className="grid flex-1 gap-px">
|
||||
<Text size={19} color="$white-100" weight="semibold">
|
||||
{title}
|
||||
</Text>
|
||||
<Text size={15} color="$white-100">
|
||||
{description}
|
||||
</Text>
|
||||
</div>
|
||||
<Button size={32} variant="darkGrey">
|
||||
{action}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { ActionCard }
|
||||
@@ -1,13 +0,0 @@
|
||||
export const Dot = () => (
|
||||
<span className="flex self-center">
|
||||
<svg
|
||||
width="2"
|
||||
height="2"
|
||||
viewBox="0 0 2 2"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="1" cy="1" r="1" fill="#647084" />
|
||||
</svg>
|
||||
</span>
|
||||
)
|
||||
@@ -1,31 +0,0 @@
|
||||
export const MessariIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_2650_62112)">
|
||||
<path
|
||||
d="M11.9531 4.24242L16.0019 0V12.2667L11.9531 15.9515V4.24242Z"
|
||||
fill="#647084"
|
||||
/>
|
||||
<path
|
||||
d="M5.97656 6.37524L7.92778 7.97523L10.0253 6.13281V12.2661L5.97656 15.951V6.37524Z"
|
||||
fill="#647084"
|
||||
/>
|
||||
<path
|
||||
d="M4.07317 4.24242L0 0V16L4.07317 12.5576V4.24242Z"
|
||||
fill="#647084"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2650_62112">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { Text } from '@status-im/components'
|
||||
|
||||
import { SOCIALS } from '@/config/links'
|
||||
|
||||
import { Logo } from '../logo'
|
||||
import { AccordionMenu } from '../navigation/accordion-menu'
|
||||
import { Dot } from './components/dot'
|
||||
import { MessariIcon } from './components/messari-icon'
|
||||
|
||||
type Props = {
|
||||
hasBorderTop?: boolean
|
||||
}
|
||||
|
||||
export const FooterMobile = (props: Props) => {
|
||||
const { hasBorderTop } = props
|
||||
|
||||
return (
|
||||
<footer
|
||||
className={`block border-dashed border-neutral-80 ${
|
||||
hasBorderTop ? 'border-t' : 'border-t-0'
|
||||
} pb-12 sm:hidden`}
|
||||
>
|
||||
<div className="">
|
||||
<div className="flex flex-col px-2 pt-6">
|
||||
<div className="px-3 pb-3">
|
||||
<Logo />
|
||||
</div>
|
||||
<div className="pb-5">
|
||||
<AccordionMenu />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-2 border-t border-dashed border-neutral-80 px-6 pt-6">
|
||||
<div className="flex w-full flex-col items-start gap-2 ">
|
||||
<Text size={11} color="$neutral-50">
|
||||
© Status Research & Development GmbH
|
||||
</Text>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Text size={11} color="$neutral-40">
|
||||
Terms of use
|
||||
</Text>
|
||||
<Dot />
|
||||
<Text size={11} color="$neutral-40">
|
||||
Privacy policy
|
||||
</Text>
|
||||
<Dot />
|
||||
<Text size={11} color="$neutral-40">
|
||||
Cookies
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start gap-3">
|
||||
<div className="flex w-full items-center justify-start gap-3 py-2">
|
||||
<MessariIcon />
|
||||
<Text size={11} color="$neutral-50">
|
||||
Messari Transparency Verified
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
{Object.values(SOCIALS).map(social => {
|
||||
const IconComponent = social.icon
|
||||
return (
|
||||
<IconComponent
|
||||
key={social.name}
|
||||
size={20}
|
||||
color="$neutral-40"
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import { Text } from '@status-im/components'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import { LINKS, SOCIALS } from '@/config/links'
|
||||
|
||||
import { Logo } from '../logo'
|
||||
import { Dot } from './components/dot'
|
||||
import { MessariIcon } from './components/messari-icon'
|
||||
import { Section } from './section'
|
||||
|
||||
type Props = {
|
||||
hasBorderTop?: boolean
|
||||
}
|
||||
|
||||
const section = cva(
|
||||
[
|
||||
'border-neutral-80',
|
||||
'mb-6',
|
||||
'flex',
|
||||
'items-start',
|
||||
'border-dashed',
|
||||
'pl-6',
|
||||
'pt-6',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
hasBorderTop: {
|
||||
true: ['border-t'],
|
||||
false: ['border-t-0'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
export const Footer = (props: Props) => {
|
||||
const { hasBorderTop } = props
|
||||
|
||||
return (
|
||||
<footer className="hidden pb-3 sm:block">
|
||||
<div className="grid grid-cols-4 gap-5 sm:gap-0 lg:mb-10 lg:grid-cols-8">
|
||||
<div
|
||||
className={section({
|
||||
hasBorderTop,
|
||||
})}
|
||||
>
|
||||
<Logo />
|
||||
</div>
|
||||
{Object.entries(LINKS).map(([title, links], index) => (
|
||||
<Section
|
||||
key={title}
|
||||
title={title}
|
||||
links={links}
|
||||
hasBorderLeft={index !== 3}
|
||||
hasBorderTop={hasBorderTop}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col items-start justify-between gap-2 px-5 lg:px-6 md-lg:flex-row md-lg:items-center">
|
||||
<div className="flex items-center gap-3">
|
||||
<Text size={11} color="$neutral-50">
|
||||
© Status Research & Development GmbH
|
||||
</Text>
|
||||
<Dot />
|
||||
<div className="flex gap-3">
|
||||
<Text size={11} color="$neutral-40">
|
||||
Terms of use
|
||||
</Text>
|
||||
<Text size={11} color="$neutral-40">
|
||||
Privacy policy
|
||||
</Text>
|
||||
<Text size={11} color="$neutral-40">
|
||||
Cookies
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<MessariIcon />
|
||||
<Text size={11} color="$neutral-50">
|
||||
Messari Transparency Verified
|
||||
</Text>
|
||||
<Dot />
|
||||
</div>
|
||||
{Object.values(SOCIALS).map(social => {
|
||||
const IconComponent = social.icon
|
||||
return (
|
||||
<IconComponent key={social.name} size={20} color="$neutral-40" />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
import { Text } from '@status-im/components'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import { Link } from '../link'
|
||||
|
||||
import type { Links } from '@/config/links'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
links: Links
|
||||
hasBorderLeft?: boolean
|
||||
hasBorderTop?: boolean
|
||||
}
|
||||
|
||||
const section = cva(
|
||||
[
|
||||
'border-neutral-80',
|
||||
'relative',
|
||||
'grid',
|
||||
'gap-3',
|
||||
'border-dashed',
|
||||
'px-5',
|
||||
'pb-6',
|
||||
'pt-6',
|
||||
'lg:border-l',
|
||||
'lg:pb-0',
|
||||
],
|
||||
{
|
||||
variants: {
|
||||
hasBorderTop: {
|
||||
true: ['border-t'],
|
||||
false: ['border-t-0'],
|
||||
},
|
||||
hasBorderLeft: {
|
||||
true: ['border-l'],
|
||||
false: ['border-l-0'],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Section = (props: Props) => {
|
||||
const { title, links, hasBorderLeft, hasBorderTop } = props
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={section({
|
||||
hasBorderTop,
|
||||
hasBorderLeft,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(180deg, transparent 0%, rgba(9 16 28 / 100%))',
|
||||
}}
|
||||
className=" absolute left-[-4px] top-0 h-full w-2"
|
||||
/>
|
||||
<Text size={13} color="$neutral-50">
|
||||
{title}
|
||||
</Text>
|
||||
<ul className="grid gap-1">
|
||||
{links.map(link => (
|
||||
<li key={link.name}>
|
||||
<Link href={link.href}>
|
||||
<Text size={15} color="$white-100" weight="medium">
|
||||
{link.name}
|
||||
</Text>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Section }
|
||||