Initial commit
34
.github/workflows/manual.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: Build Manually
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7
|
||||||
|
tags: ghcr.io/marcus7i/saucekudasai:${{ github.ref_name }},ghcr.io/marcus7i/saucekudasai:latest
|
||||||
35
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
name: Build on Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [created]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7
|
||||||
|
tags: ghcr.io/marcus7i/saucekudasai:${{ github.ref_name }},ghcr.io/marcus7i/saucekudasai:latest
|
||||||
26
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
/.vscode
|
||||||
|
/.eslintcache
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
7
Dockerfile
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
FROM node:16-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json .
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
EXPOSE 3006
|
||||||
|
CMD ["npm", "run"]
|
||||||
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Ayush
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
44
README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!-- @format -->
|
||||||
|
|
||||||
|
<h1 align="center">SauceKudasai</h1>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> A Frontend Search Engine that fetches anime info based on the image provided using <a href="https://github.com/soruly/trace.moe" target="_blank">trace.moe </a> and <a href="https://anilist.gitbook.io/anilist-apiv2-docs/" target="_blank"> AniList</a> API
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Development:
|
||||||
|
- Node.JS
|
||||||
|
|
||||||
|
### Deployment:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
npm run getsauce
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment via docker:
|
||||||
|
|
||||||
|
Using image:
|
||||||
|
```sh
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Building image:
|
||||||
|
```sh
|
||||||
|
docker compose -f docker-compose-build.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Original: **Ayush Gupta**<br>
|
||||||
|
- Github: https://github.com/ayushgptaa
|
||||||
|
|
||||||
|
Modified by: **MarcUs7i**<br>
|
||||||
|
- GitHub: https://github.com/MarcUs7i
|
||||||
|
|
||||||
|
Original repository: https://github.com/ayushgptaa/SauceKudasai
|
||||||
8
docker-compose-build.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "3006:3006"
|
||||||
|
command: "npm start"
|
||||||
7
docker-compose.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
services:
|
||||||
|
saucekudasai:
|
||||||
|
image: ghcr.io/marcus7i/saucekudasai:latest
|
||||||
|
container_name: saucekudasai
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3006:3006"
|
||||||
5
jsconfig.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./src"
|
||||||
|
}
|
||||||
|
}
|
||||||
35105
package-lock.json
generated
Normal file
56
package.json
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"name": "saucekudasai",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "A Anime search engine that fetches anime info based on the image provided.",
|
||||||
|
"keywords": [
|
||||||
|
"Anime",
|
||||||
|
"React",
|
||||||
|
"Anilist",
|
||||||
|
"Search-Engine",
|
||||||
|
"Trace-moe",
|
||||||
|
"anime-search"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/MarcUs7i/SauceKudasai"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.2.0",
|
||||||
|
"@testing-library/user-event": "^14.6.1",
|
||||||
|
"axios": "^1.8.3",
|
||||||
|
"framer-motion": "^12.5.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"react-dropzone": "^14.3.8",
|
||||||
|
"react-icons": "^5.5.0",
|
||||||
|
"react-scripts": "^5.0.1",
|
||||||
|
"styled-components": "^6.1.16",
|
||||||
|
"web-vitals": "^4.2.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"getsauce": "set PORT=3006 && react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/SauceKudasai.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/favicon/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 904 B |
BIN
public/favicon/favicon-192x192.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
public/favicon/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
public/favicon/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/icons/icon-180x180.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
public/icons/icon-256x256.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
public/icons/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 245 KiB |
BIN
public/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 398 KiB |
94
public/index.html
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
<!-- @format -->
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
|
<link rel="icon" href="favicon/favicon.ico" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="favicon//favicon-32x32.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="favicon//favicon-16x16.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="192x19" href="favicon/favicon-192x192.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" type="image/png" href="icons/icon-180x180.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="192x192" type="image/png" href="icons/icon-192x192.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="256x256" type="image/png" href="icons/icon-1256x256.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="382-384" type="image/png" href="icons/icon-382-384.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="512x512" type="image/png" href="icons/icon-512x512.png" />
|
||||||
|
<link rel="manifest" href="manifest.json" />
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="application-name" content="Saucekudasai" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Saucekudasai" />
|
||||||
|
<meta name="theme-color" content="#E5DFF6" />
|
||||||
|
<meta name="msapplication-navbutton-color" content="#E5DFF6" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="#E5DFF6" />
|
||||||
|
<meta name="msapplication-starturl" content="." />
|
||||||
|
|
||||||
|
<!-- Primary Meta Tags -->
|
||||||
|
<title>SauceKudasai - Search any Anime by Image</title>
|
||||||
|
<meta name="title" content="SauceKudasai - Search any Anime by Image" />
|
||||||
|
<meta name="description" content=" Anime search engine that fetches anime info based on the image provided." />
|
||||||
|
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="Saucekudasai, saucekudasai, SauceKudasai.com, sauce, what anime, myanimelist, Anime, free, Search, Tracemoe, Anilist, image, Demon slayer, アニメ, Anime scene search, Image Search, Search Engine"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Open Graph / Facebook -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://saucekudasai.com/" />
|
||||||
|
<meta property="og:title" content="SauceKudasai - Search any Anime by Image" />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Anime search engine that fetches anime info based on the image provided."
|
||||||
|
/>
|
||||||
|
<meta property="og:image" content="https://saucekudasai.com/SauceKudasai.png" />
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
|
<meta property="twitter:url" content="https://saucekudasai.com" />
|
||||||
|
<meta property="twitter:title" content="SauceKudasai - Search any Anime by Image" />
|
||||||
|
<meta
|
||||||
|
property="twitter:description"
|
||||||
|
content="Anime search engine that fetches anime info based on the image provided."
|
||||||
|
/>
|
||||||
|
<meta property="twitter:image" content="https://saucekudasai.com/SauceKudasai.png" />
|
||||||
|
<meta name="twitter:image:alt" content="SauceKudasai - Search any Anime by Image" />
|
||||||
|
<meta name="twitter:creator" content="@ayushgptaa" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||||
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap"
|
||||||
|
rel="stylesheet"
|
||||||
|
/>
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-98879STR66"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag() {
|
||||||
|
dataLayer.push(arguments);
|
||||||
|
}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-98879STR66');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3151427902838816" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#root {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 100vh;
|
||||||
|
background: linear-gradient(116.2deg, #d9e5fa -0.48%, #fad9f3 102.36%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
40
public/manifest.json
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"short_name": "Saucekudasai",
|
||||||
|
"name": "Saucekudasai ",
|
||||||
|
"lang": "en",
|
||||||
|
"description": "An anime search engine that provide animeinfo based on image or url provided using trace.moe Api",
|
||||||
|
"background_color": "#E5DFF6",
|
||||||
|
"theme_color": "#E5DFF6",
|
||||||
|
"dir": "auto",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"start_url": ".",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "icons/icon-180x180.png",
|
||||||
|
"sizes": "180x180",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/icon-256x256.png",
|
||||||
|
"sizes": "256x256",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "icons/icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"prefer_related_applications": false
|
||||||
|
}
|
||||||
53
src/Api/constant.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export const TRACE_MOE_QUERY = 'https://api.trace.moe/search';
|
||||||
|
|
||||||
|
export const ANILIST_QUERY = 'https://graphql.anilist.co';
|
||||||
|
|
||||||
|
// General options for Axios instance
|
||||||
|
export const options = {
|
||||||
|
headers: {
|
||||||
|
' Accept': 'application/json',
|
||||||
|
"x-trace-key": process.env.API_KEY,
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Axios instance
|
||||||
|
*any data provided in instance.get will be merged with options
|
||||||
|
*/
|
||||||
|
export const instance = axios.create({
|
||||||
|
baseURL: TRACE_MOE_QUERY,
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Query data for Anilist GraphQL Api
|
||||||
|
export const query = `
|
||||||
|
query ($id: Int) {
|
||||||
|
Media (id:$id , type: ANIME) {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
description(asHtml:false)
|
||||||
|
seasonYear
|
||||||
|
coverImage {
|
||||||
|
large
|
||||||
|
}
|
||||||
|
bannerImage
|
||||||
|
genres
|
||||||
|
externalLinks {
|
||||||
|
id
|
||||||
|
url
|
||||||
|
site
|
||||||
|
}
|
||||||
|
averageScore
|
||||||
|
siteUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
`;
|
||||||
68
src/App.js
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
/** @format */
|
||||||
|
import { useEffect, useContext } from 'react';
|
||||||
|
import Fileupload from 'Components/Fileupload/Fileupload';
|
||||||
|
import Results from 'Components/Resultcard/Results';
|
||||||
|
import { GlobalStyle } from 'styles/GlobalStyle';
|
||||||
|
import Navbar from 'Components/Navbar';
|
||||||
|
import { Footertext } from 'Components/Footer/Footertext';
|
||||||
|
import { ContextProvider, Context } from 'store/Context-Provider';
|
||||||
|
import { ServerError, UserError } from 'Components/Ui/Errorcard';
|
||||||
|
|
||||||
|
function AppContent() {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.style.height = window.innerHeight + 'px';
|
||||||
|
const setheight = () => {
|
||||||
|
document.body.style.height = window.innerHeight + 'px';
|
||||||
|
};
|
||||||
|
window.addEventListener('resize', setheight);
|
||||||
|
|
||||||
|
// Clipboard paste event listener
|
||||||
|
const handlePaste = (e) => {
|
||||||
|
const items = e.clipboardData?.items;
|
||||||
|
if (!items) return;
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type.indexOf('image') !== -1) {
|
||||||
|
const file = items[i].getAsFile();
|
||||||
|
|
||||||
|
// Pass as array
|
||||||
|
ctx.imagehandler([file]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('paste', handlePaste);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', setheight);
|
||||||
|
window.removeEventListener('paste', handlePaste);
|
||||||
|
};
|
||||||
|
}, [ctx]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Fileupload />
|
||||||
|
<Results />
|
||||||
|
<UserError />
|
||||||
|
<ServerError />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div className="App">
|
||||||
|
<GlobalStyle />
|
||||||
|
<Navbar />
|
||||||
|
<ContextProvider>
|
||||||
|
<AppContent />
|
||||||
|
</ContextProvider>
|
||||||
|
<Footertext />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
85
src/Components/Fileupload/DropZone/DropZone.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import Dropzone from 'react-dropzone';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Previewimage from 'Components/Preview/Previewimage';
|
||||||
|
import { Imagecontainer, respondTo } from 'styles/mixins';
|
||||||
|
import Urlinput from 'Components/Ui/Urlinput';
|
||||||
|
import { Context } from 'store/Context-Provider';
|
||||||
|
|
||||||
|
const Dropcontainer = styled.div`
|
||||||
|
color: #000;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 75%;
|
||||||
|
margin: 0.3rem;
|
||||||
|
background: var(--lavenderlight);
|
||||||
|
border-radius: 20px;
|
||||||
|
user-select: none;
|
||||||
|
padding-top: 1.1rem;
|
||||||
|
${respondTo.sm`
|
||||||
|
height: 80%;
|
||||||
|
`}
|
||||||
|
${respondTo.md`
|
||||||
|
height: 75%;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg`
|
||||||
|
height: 78%;
|
||||||
|
border-radius: 25px;
|
||||||
|
margin: 0.8rem 1.5rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PreviewContainer = styled(Imagecontainer)`
|
||||||
|
position: relative;
|
||||||
|
height: 120px;
|
||||||
|
width: 200px;
|
||||||
|
margin: auto;
|
||||||
|
position: relative;
|
||||||
|
border-radius: calc(var(--radius) / 2);
|
||||||
|
${respondTo.xs`
|
||||||
|
height: 150px;
|
||||||
|
width: 250px;
|
||||||
|
`}
|
||||||
|
${respondTo.sm`
|
||||||
|
height: 160px;
|
||||||
|
width: 280px;
|
||||||
|
`}
|
||||||
|
/* ${respondTo.md`
|
||||||
|
height: 200px;
|
||||||
|
width: 330px;
|
||||||
|
`} */
|
||||||
|
${respondTo.lg`{
|
||||||
|
height: 75%;
|
||||||
|
width: 45%;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
const DropZone = ({ showurl, toggleurl }) => {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
const { imagehandler, urlhandler, url } = ctx;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dropzone onDrop={imagehandler} accept="image/*" multiple={false} noClick={true}>
|
||||||
|
{({ getRootProps, getInputProps, open }) => (
|
||||||
|
<Dropcontainer {...getRootProps()}>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
<PreviewContainer>
|
||||||
|
<Previewimage open={open} />
|
||||||
|
</PreviewContainer>
|
||||||
|
<Urlinput
|
||||||
|
url={url}
|
||||||
|
toggleurl={toggleurl}
|
||||||
|
urlhandler={urlhandler}
|
||||||
|
showurl={showurl}
|
||||||
|
open={open}
|
||||||
|
/>
|
||||||
|
{/* <Filebtn open={open} toggleurl={toggleurl} showurl={showurl} /> */}
|
||||||
|
</Dropcontainer>
|
||||||
|
)}
|
||||||
|
</Dropzone>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DropZone;
|
||||||
21
src/Components/Fileupload/Fileupload.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/** @format */
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Searchbtn from '../Ui/Searchbtn';
|
||||||
|
import Dropzone from './DropZone/DropZone';
|
||||||
|
import { Container } from './Fileuploadstyle';
|
||||||
|
|
||||||
|
export const Fileupload = () => {
|
||||||
|
const [showurl, setShowurl] = useState(false);
|
||||||
|
const toggleurl = () => {
|
||||||
|
setShowurl(prevstate => !prevstate);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Dropzone showurl={showurl} toggleurl={toggleurl} />
|
||||||
|
<Searchbtn />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Fileupload;
|
||||||
48
src/Components/Fileupload/Fileuploadstyle.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { respondTo } from '../../styles/mixins';
|
||||||
|
|
||||||
|
export const Container = styled.div`
|
||||||
|
background: var(--primary);
|
||||||
|
width: 88%;
|
||||||
|
height: 310px;
|
||||||
|
padding: 1.3rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
margin: 1.5em auto;
|
||||||
|
text-align: center;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px 5px 29px -4px rgba(0, 0, 0, 0.25), inset 0px 1px 5px rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 30px;
|
||||||
|
|
||||||
|
& p {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
${respondTo.xs`
|
||||||
|
padding-bottom:0;
|
||||||
|
height: 320px;
|
||||||
|
width: 400px;
|
||||||
|
`}
|
||||||
|
${respondTo.sm`
|
||||||
|
height: 350px;
|
||||||
|
width: 450px;
|
||||||
|
padding:1.7rem;
|
||||||
|
padding-bottom:1rem;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.md`
|
||||||
|
height: 350px;
|
||||||
|
width: 550px;
|
||||||
|
padding-bottom:0rem;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg` {
|
||||||
|
margin-top: 30px;
|
||||||
|
height: 370px;
|
||||||
|
width: 750px;
|
||||||
|
padding:1rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
35
src/Components/Footer/Footertext.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Footer = styled.footer`
|
||||||
|
letter-spacing: 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #000;
|
||||||
|
font-weight: var(--semi-bold);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
@media (min-width: 500px) {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: var(--medium);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const Footertext = () => {
|
||||||
|
return (
|
||||||
|
<Footer>
|
||||||
|
<p>
|
||||||
|
Powered by
|
||||||
|
<a href="https://github.com/soruly/trace.moe" target="_blank" rel="noreferrer">
|
||||||
|
trace.moe
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</Footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
65
src/Components/Navbar/index.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { IconContext } from 'react-icons';
|
||||||
|
import { BsGithub } from 'react-icons/bs';
|
||||||
|
import { respondTo } from 'styles/mixins';
|
||||||
|
|
||||||
|
const Navbar = styled.nav`
|
||||||
|
padding: 0.5em 1.5rem;
|
||||||
|
background: var(--nav);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 30px;
|
||||||
|
width: 90%;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 1rem;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
filter: drop-shadow(0px 4px 10px rgba(0, 0, 0, 0.25));
|
||||||
|
|
||||||
|
${respondTo.xs`
|
||||||
|
width: 400px;
|
||||||
|
`}
|
||||||
|
${respondTo.sm`
|
||||||
|
width: 450px;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg`
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 700px;
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = styled.h1`
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--medium);
|
||||||
|
color: #000;
|
||||||
|
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 1.2rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const index = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Navbar>
|
||||||
|
<Title>SauceKudasai</Title>
|
||||||
|
<a
|
||||||
|
href="https://github.com/MarcUs7i/SauceKudasai"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
aria-label="Github link">
|
||||||
|
<IconContext.Provider value={{ size: '1.4rem', color: '#000000' }}>
|
||||||
|
<BsGithub />
|
||||||
|
</IconContext.Provider>
|
||||||
|
</a>
|
||||||
|
</Navbar>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default index;
|
||||||
21
src/Components/Preview/PreviewStyles.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Image = styled.img`
|
||||||
|
border-radius: calc(var(--radius) / 3);
|
||||||
|
`;
|
||||||
|
export const Loadingimg = styled(Image)`
|
||||||
|
filter: blur(2.5px);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Video = styled.video`
|
||||||
|
border-radius: calc(var(--radius) / 2);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Mutebtn = styled.button`
|
||||||
|
background: transparent;
|
||||||
|
position: absolute;
|
||||||
|
right: 6px;
|
||||||
|
bottom: 5px;
|
||||||
|
`;
|
||||||
69
src/Components/Preview/Previewimage.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
|
import { Loader } from 'Components/Ui/loader';
|
||||||
|
import { Image, Loadingimg, Video, Mutebtn } from './PreviewStyles';
|
||||||
|
import { Uploadinfo } from 'Components/Ui/Uploadinfo/Uploadinfo';
|
||||||
|
import { Context } from 'store/Context-Provider';
|
||||||
|
import { IconContext } from 'react-icons';
|
||||||
|
import { GoUnmute, GoMute } from 'react-icons/go';
|
||||||
|
|
||||||
|
const Previewimage = ({ open }) => {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
const { image, url, loading, video } = ctx;
|
||||||
|
const [preview, setpreview] = useState(null);
|
||||||
|
const [mute, setmute] = useState(true);
|
||||||
|
const mutehandler = () => {
|
||||||
|
setmute(prevstate => !prevstate);
|
||||||
|
};
|
||||||
|
// This is use to set preview to the image selected by the user
|
||||||
|
useEffect(() => {
|
||||||
|
if (image) return setpreview(URL.createObjectURL(image));
|
||||||
|
setmute(true);
|
||||||
|
}, [image]);
|
||||||
|
|
||||||
|
// This is use to set preview to the url selected by the user
|
||||||
|
useEffect(() => {
|
||||||
|
if (url) setpreview(url);
|
||||||
|
setmute(true);
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
/* This code Checks first if the the video Exits
|
||||||
|
If the video exits the video tag is set to the video
|
||||||
|
If the video doesnot exits then it checks if the loading exits
|
||||||
|
If loading exits then loading is rendered on the screen otherwise
|
||||||
|
preview is rendered */
|
||||||
|
<>
|
||||||
|
{video === null ? (
|
||||||
|
loading ? (
|
||||||
|
<>
|
||||||
|
<Loadingimg src={preview} alt="Loading..." />
|
||||||
|
<Loader />
|
||||||
|
</>
|
||||||
|
) : preview ? (
|
||||||
|
<Image src={preview} alt="Your Search image" onClick={e => e.stopPropagation()} />
|
||||||
|
) : (
|
||||||
|
<Uploadinfo open={open} />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Video autoPlay loop muted={mute} src={video}></Video>
|
||||||
|
<Mutebtn onClick={mutehandler}>
|
||||||
|
{mute ? (
|
||||||
|
<IconContext.Provider value={{ size: '1.3rem', color: '#d9d9f9' }}>
|
||||||
|
<GoMute></GoMute>
|
||||||
|
</IconContext.Provider>
|
||||||
|
) : (
|
||||||
|
<IconContext.Provider value={{ size: '1.3rem', color: '#d9d9f9' }}>
|
||||||
|
<GoUnmute></GoUnmute>
|
||||||
|
</IconContext.Provider>
|
||||||
|
)}
|
||||||
|
</Mutebtn>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Previewimage;
|
||||||
125
src/Components/Resultcard/Results.js
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
/** @format */
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { Context } from 'store/Context-Provider';
|
||||||
|
import { AiOutlineInfoCircle } from 'react-icons/ai';
|
||||||
|
import { IoIosArrowForward } from 'react-icons/io';
|
||||||
|
import Overlay from '../Ui/Overlay';
|
||||||
|
import { Closebtn } from 'Components/Ui/Closebtn';
|
||||||
|
import { AnimatePresence } from 'framer-motion';
|
||||||
|
import {
|
||||||
|
Animecard,
|
||||||
|
Bannerimg,
|
||||||
|
Banner,
|
||||||
|
Banneroverlay,
|
||||||
|
Cover,
|
||||||
|
Coverimg,
|
||||||
|
Animeinfo,
|
||||||
|
Animetext,
|
||||||
|
Title,
|
||||||
|
Info,
|
||||||
|
Details,
|
||||||
|
Summary,
|
||||||
|
Links,
|
||||||
|
Resultfooter,
|
||||||
|
Moreinfo,
|
||||||
|
StyledLink,
|
||||||
|
Similarity,
|
||||||
|
} from './Resultstyle';
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
initial: { y: '100%' },
|
||||||
|
animate: {
|
||||||
|
y: '0',
|
||||||
|
transition: {
|
||||||
|
duration: 0.5,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
type: 'linear',
|
||||||
|
delay: 0.1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
y: '100%',
|
||||||
|
transition: {
|
||||||
|
delay: 0.1,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
type: 'linear',
|
||||||
|
duration: 0.8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const Results = () => {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
const truncate = (str, num) => {
|
||||||
|
if (str.length <= num) return str;
|
||||||
|
return str.substring(0, num).concat('...');
|
||||||
|
};
|
||||||
|
const closeResult = () => {
|
||||||
|
ctx.cardhandler();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AnimatePresence>
|
||||||
|
{ctx.animeinfoexits ? (
|
||||||
|
<>
|
||||||
|
<Overlay key="Overlay" onClick={closeResult} />
|
||||||
|
<Animecard key="animecard" variants={variants} initial="initial" animate="animate" exit="exit">
|
||||||
|
<Banner>
|
||||||
|
<Closebtn onClick={closeResult}></Closebtn>
|
||||||
|
{ctx.animeinfo.bannerImage ? (
|
||||||
|
<>
|
||||||
|
<Bannerimg src={ctx.animeinfo.bannerImage}></Bannerimg>
|
||||||
|
<Banneroverlay></Banneroverlay>{' '}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</Banner>
|
||||||
|
<Animeinfo>
|
||||||
|
<Cover>
|
||||||
|
<Coverimg src={ctx.animeinfo.coverImage.large} alt=""></Coverimg>
|
||||||
|
</Cover>
|
||||||
|
<Animetext>
|
||||||
|
<Title>{ctx.animeinfo.title.english || ctx.animeinfo.title.native}</Title>
|
||||||
|
<Info>
|
||||||
|
<Details>
|
||||||
|
<h3>Ep {ctx.animeinfo.episode}</h3>
|
||||||
|
<h3>at {(ctx.animeinfo.time / 60).toFixed(2).replace('.', ':')}</h3>
|
||||||
|
<h3>{ctx.animeinfo.seasonYear}</h3>
|
||||||
|
<Similarity props={ctx.animeinfo.similarity.toFixed(2) * 100}>
|
||||||
|
{ctx.animeinfo.similarity.toFixed(2) * 100}%
|
||||||
|
</Similarity>
|
||||||
|
</Details>
|
||||||
|
</Info>
|
||||||
|
<Summary>
|
||||||
|
<p>{ctx.animeinfo.description ? truncate(ctx.animeinfo.description, 250) : null}</p>
|
||||||
|
<Links>
|
||||||
|
{ctx.animeinfo.externalLinks.map(({ id, site, url }) => {
|
||||||
|
return (
|
||||||
|
<li key={id}>
|
||||||
|
<StyledLink href={url} target="_blank">
|
||||||
|
{site}
|
||||||
|
</StyledLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Links>
|
||||||
|
</Summary>
|
||||||
|
</Animetext>
|
||||||
|
<Resultfooter>
|
||||||
|
<Moreinfo href={ctx.animeinfo.siteUrl} target="_blank">
|
||||||
|
<AiOutlineInfoCircle size={15} />
|
||||||
|
<span>More Info</span>
|
||||||
|
<IoIosArrowForward size={15} />
|
||||||
|
</Moreinfo>
|
||||||
|
<span>
|
||||||
|
Information by{' '}
|
||||||
|
<StyledLink href="https://anilist.com" target="_blank">
|
||||||
|
Anilist
|
||||||
|
</StyledLink>
|
||||||
|
</span>
|
||||||
|
</Resultfooter>
|
||||||
|
</Animeinfo>
|
||||||
|
</Animecard>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Results;
|
||||||
248
src/Components/Resultcard/Resultstyle.js
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { respondTo } from 'styles/mixins';
|
||||||
|
|
||||||
|
export const Animecard = styled(motion.div)`
|
||||||
|
z-index: 99;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
height: 50%;
|
||||||
|
width: 100vw;
|
||||||
|
background: var(--cardbg);
|
||||||
|
border-radius: var(--card-radius);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Banner = styled.div`
|
||||||
|
background-color: var(--cardbg);
|
||||||
|
height: 30%;
|
||||||
|
position: relative;
|
||||||
|
border-radius: var(--card-radius);
|
||||||
|
${respondTo.lg`
|
||||||
|
height: 35%;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
export const Bannerimg = styled.img`
|
||||||
|
border-radius: var(--card-radius);
|
||||||
|
background-color: var(--cardbg);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Banneroverlay = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
z-index: 2;
|
||||||
|
border-radius: var(--card-radius);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Cover = styled.div`
|
||||||
|
width: 120px;
|
||||||
|
height: 170px;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
z-index: 3;
|
||||||
|
position: relative;
|
||||||
|
bottom: 30px;
|
||||||
|
|
||||||
|
${respondTo.sm`
|
||||||
|
width: 140px;
|
||||||
|
height: 200px;
|
||||||
|
`}
|
||||||
|
${respondTo.tab`
|
||||||
|
width: 160px;
|
||||||
|
height: 230px;
|
||||||
|
bottom: 50px;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.tab`
|
||||||
|
width: 160px;
|
||||||
|
height: 230px;
|
||||||
|
bottom: 60px;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg`
|
||||||
|
width: 180px;
|
||||||
|
height: 250px;
|
||||||
|
bottom:85px;
|
||||||
|
`}
|
||||||
|
${respondTo.xl`
|
||||||
|
width: 210px;
|
||||||
|
height: 270px;
|
||||||
|
bottom:90px;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Coverimg = styled.img`
|
||||||
|
border-radius: inherit;
|
||||||
|
`;
|
||||||
|
export const Animeinfo = styled.div`
|
||||||
|
padding: 0 0.8rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-column-gap: 10px;
|
||||||
|
${respondTo.tab`
|
||||||
|
gap: 20px;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
export const Animetext = styled.div`
|
||||||
|
padding-top: 0.4rem;
|
||||||
|
color: var(--lavender);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Title = styled.h1`
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: var(--bold);
|
||||||
|
${respondTo.md`
|
||||||
|
font-size: 1.8rem;
|
||||||
|
`}
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 2.3rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Info = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
export const Details = styled.div`
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
${respondTo.tab`
|
||||||
|
font-size: 0.7rem;
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
`}
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 1rem;
|
||||||
|
width:60%;
|
||||||
|
& > h3 {
|
||||||
|
&:nth-child(2) {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
&:nth-child(3) {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
&:nth-child(4) {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
& > h3 {
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const Similarity = styled.h3`
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
color: ${props => (props.props > 90 ? '#15f115' : '#c7e423')};
|
||||||
|
`;
|
||||||
|
export const Summary = styled.div`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: auto;
|
||||||
|
margin-top: 0.7rem;
|
||||||
|
overflow: hidden;
|
||||||
|
${respondTo.tab`
|
||||||
|
grid-gap:15px;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg`
|
||||||
|
grid-template-columns: 66% 30%;
|
||||||
|
grid-gap:45px;
|
||||||
|
`}
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
display: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: var(--regular);
|
||||||
|
${respondTo.tab`
|
||||||
|
display:block;
|
||||||
|
`}
|
||||||
|
${respondTo.md`
|
||||||
|
font-size: 0.9rem;
|
||||||
|
`}
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 0.9rem;
|
||||||
|
`}
|
||||||
|
${respondTo.xl`
|
||||||
|
font-size: 1.1rem;
|
||||||
|
`}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const Genre = styled.div`
|
||||||
|
display: none;
|
||||||
|
width: 30%;
|
||||||
|
${respondTo.xl`
|
||||||
|
// display:block;
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
export const Links = styled.div`
|
||||||
|
font-size: 0.85rem;
|
||||||
|
overflow: hidden;
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 0.9rem;
|
||||||
|
`}
|
||||||
|
${respondTo.xl`
|
||||||
|
font-size: 1.1rem;
|
||||||
|
`}
|
||||||
|
& > li {
|
||||||
|
padding-right: 0.6rem;
|
||||||
|
padding-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const Resultfooter = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
|
||||||
|
${respondTo.tab`
|
||||||
|
padding: 0.8rem;
|
||||||
|
`}
|
||||||
|
${respondTo.lg`
|
||||||
|
padding: 0.6rem 1.5rem;
|
||||||
|
`}
|
||||||
|
& > span {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Moreinfo = styled.a`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
gap: 4px;
|
||||||
|
& > span {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 1rem;
|
||||||
|
`}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const StyledLink = styled.a`
|
||||||
|
color: var(--Styledlinks);
|
||||||
|
`;
|
||||||
|
export const Closebtn = styled.div`
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.6rem;
|
||||||
|
right: 0.8rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 99;
|
||||||
|
`;
|
||||||
26
src/Components/Ui/Closebtn.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { IoMdClose } from 'react-icons/io';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const CloseBtn = styled.div`
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.6rem;
|
||||||
|
right: 0.8rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 99;
|
||||||
|
`;
|
||||||
|
export const Closebtn = ({ onClick }) => {
|
||||||
|
return (
|
||||||
|
<CloseBtn onClick={onClick}>
|
||||||
|
<IoMdClose color={'black'} size={20} />
|
||||||
|
</CloseBtn>
|
||||||
|
);
|
||||||
|
};
|
||||||
127
src/Components/Ui/Errorcard.js
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import Overlay from '../Ui/Overlay';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Chikagif from 'Components/Ui/images/chika.gif';
|
||||||
|
import Kaguyasama from 'Components/Ui/images/kaguya-sama.gif';
|
||||||
|
import { respondTo } from '../../styles/mixins';
|
||||||
|
import { Closebtn } from './Closebtn';
|
||||||
|
import { Context } from 'store/Context-Provider';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
|
||||||
|
const Container = styled(motion.div)`
|
||||||
|
width: 280px;
|
||||||
|
height: 280px;
|
||||||
|
z-index: 200;
|
||||||
|
background: var(--cardbg);
|
||||||
|
border-radius: 15px;
|
||||||
|
position: absolute;
|
||||||
|
top: 45%;
|
||||||
|
left: 50%;
|
||||||
|
|
||||||
|
${respondTo.xs`
|
||||||
|
height: 320px;
|
||||||
|
width: 400px;
|
||||||
|
`}
|
||||||
|
${respondTo.sm`
|
||||||
|
height: 400px;
|
||||||
|
width: 450px;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg` {
|
||||||
|
height: 450px;
|
||||||
|
width: 600px;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Gif = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 15px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 15px 15px 0 0;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Errormsg = styled.div`
|
||||||
|
text-align: center;
|
||||||
|
color: var(--lavender);
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: var(--regular);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
${respondTo.sm`
|
||||||
|
font-size: 1.5rem;
|
||||||
|
`}
|
||||||
|
${respondTo.lg`
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
`}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
const variants = {
|
||||||
|
initial: { scale: 0, x: '-50%', y: '-50%' },
|
||||||
|
animate: {
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.9,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
type: 'spring',
|
||||||
|
delay: 0.1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
scale: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.6,
|
||||||
|
delay: 0.1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export const Errorcard = ({ image, error, errormsg }) => {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
return (
|
||||||
|
<AnimatePresence>
|
||||||
|
{error ? (
|
||||||
|
<>
|
||||||
|
<Overlay />
|
||||||
|
<Container variants={variants} key="Errorcard" initial="initial" animate="animate" exit="exit">
|
||||||
|
<Gif>
|
||||||
|
<img src={image} alt="Server Error" />
|
||||||
|
<Closebtn
|
||||||
|
onClick={() => {
|
||||||
|
ctx.errorhandler();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Gif>
|
||||||
|
<Errormsg>
|
||||||
|
<h3>{errormsg}</h3>
|
||||||
|
</Errormsg>
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UserError = () => {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
const error = ctx.usererror;
|
||||||
|
return (
|
||||||
|
<Errorcard
|
||||||
|
image={Kaguyasama}
|
||||||
|
errormsg="Looks like you are offline or using wrong image/url 👉👈"
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const ServerError = () => {
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
const error = ctx.servererror;
|
||||||
|
return <Errorcard image={Chikagif} errormsg="Saba-chan is a little busy at the moment 😓" error={error} />;
|
||||||
|
};
|
||||||
51
src/Components/Ui/Filebtn.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { FiFolderPlus, FiLink2 } from 'react-icons/fi';
|
||||||
|
import { IconContext } from 'react-icons';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { respondTo } from 'styles/mixins';
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
position: relative;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
border: none;
|
||||||
|
background: var(--lavender);
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 1.4rem 0.5rem 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
& > svg {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
${respondTo.xs`
|
||||||
|
margin: 0.6rem 0.6rem 0 0;
|
||||||
|
`}
|
||||||
|
${respondTo.sm`
|
||||||
|
margin: 1rem 0.6rem 0 0;
|
||||||
|
`}
|
||||||
|
${respondTo.md`
|
||||||
|
margin: 0.8rem 0.6rem 0 0;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Filebtn = ({ open, toggleurl, key1, key2 }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button onClick={open} key={key1} aria-label="Fileupload button">
|
||||||
|
<IconContext.Provider value={{ size: '1.1rem', color: '#303133' }}>
|
||||||
|
<FiFolderPlus />
|
||||||
|
</IconContext.Provider>
|
||||||
|
</Button>
|
||||||
|
<Button onClick={toggleurl} key={key2} aria-label="Url button">
|
||||||
|
<IconContext.Provider value={{ size: '1.1rem', color: '#303133' }}>
|
||||||
|
<FiLink2 />
|
||||||
|
</IconContext.Provider>
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
21
src/Components/Ui/Overlay.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/** @format */
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Overlay = styled.div`
|
||||||
|
position: fixed;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: 98;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const overlay = ({ onClick }) => {
|
||||||
|
return <Overlay onClick={onClick} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default overlay;
|
||||||
49
src/Components/Ui/Searchbtn.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/** @format */
|
||||||
|
import { useContext, useState, useEffect } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { respondTo } from '../../styles/mixins';
|
||||||
|
import { Context } from 'store/Context-Provider';
|
||||||
|
|
||||||
|
const Search = styled.button`
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem 1.7rem;
|
||||||
|
background: var(--lavender);
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
color: #000;
|
||||||
|
border-radius: calc(var(--radius) * 2);
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
letter-spacing: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
filter: drop-shadow(0px 2px 3px rgba(0, 0, 0, 0.25));
|
||||||
|
cursor: ${props => (props.image || props.url ? 'pointer' : 'not-allowed')};
|
||||||
|
|
||||||
|
${respondTo.md`
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.9rem 2rem;
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg`
|
||||||
|
margin-top: 0rem;
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Searchbtn = () => {
|
||||||
|
const [disable, setdisable] = useState(true);
|
||||||
|
const ctx = useContext(Context);
|
||||||
|
const { image, url, fileUpload } = ctx;
|
||||||
|
useEffect(() => {
|
||||||
|
if (image || url) return setdisable(false);
|
||||||
|
}, [image, url]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Search onClick={fileUpload} type="submit" disabled={disable} image={image} url={url}>
|
||||||
|
Search
|
||||||
|
</Search>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Searchbtn;
|
||||||
65
src/Components/Ui/Uploadinfo/Uploadinfo.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import Upload from '../images/Ei-share-apple.svg';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { respondTo } from '../../../styles/mixins';
|
||||||
|
|
||||||
|
export const Uploadimg = styled.img`
|
||||||
|
height: 65px;
|
||||||
|
width: 40px;
|
||||||
|
opacity: 75%;
|
||||||
|
|
||||||
|
${respondTo.sm`
|
||||||
|
height: 80px;
|
||||||
|
width: 60px;
|
||||||
|
|
||||||
|
`}
|
||||||
|
|
||||||
|
${respondTo.lg`{
|
||||||
|
margin-top: 0.9rem;
|
||||||
|
height: 80px;
|
||||||
|
width: 60px;
|
||||||
|
opacity: 75%;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
export const Uploadtext = styled.div`
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
letter-spacing: 0px;
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
opacity: 80%;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: var(--lightblue);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
${respondTo.xs`
|
||||||
|
font-size: 1.4rem;
|
||||||
|
`}
|
||||||
|
${respondTo.md`
|
||||||
|
font-size: 1.3rem;
|
||||||
|
`}
|
||||||
|
${respondTo.lg`
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
font-size: 1.6rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PasteHint = styled.div`
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 70%;
|
||||||
|
font-style: italic;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Uploadinfo = ({ open }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Uploadimg src={Upload} alt="Upload" />
|
||||||
|
<Uploadtext>
|
||||||
|
Drop your images, <span onClick={open}> browse </span> or import from
|
||||||
|
</Uploadtext>
|
||||||
|
<PasteHint>Or paste from clipboard (Ctrl+V)</PasteHint>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
116
src/Components/Ui/Urlinput.js
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { AnimatePresence } from 'framer-motion';
|
||||||
|
import { Filebtn } from 'Components/Ui/Filebtn';
|
||||||
|
import { respondTo } from 'styles/mixins';
|
||||||
|
import { IoMdClose } from 'react-icons/io';
|
||||||
|
|
||||||
|
const UrlContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
height: 40px;
|
||||||
|
width: 220px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1.4rem;
|
||||||
|
|
||||||
|
${respondTo.xs`
|
||||||
|
margin: 0.6rem 0.6rem 0 0;
|
||||||
|
`}
|
||||||
|
${respondTo.sm`
|
||||||
|
margin-top: 1rem;
|
||||||
|
`}
|
||||||
|
${respondTo.md`
|
||||||
|
margin-top: 0.8rem ;
|
||||||
|
font-size: 1rem;
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
const Url = styled(motion.input)`
|
||||||
|
position: relative;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: var(--lavender);
|
||||||
|
padding: 0rem 2.5rem;
|
||||||
|
height: 40px;
|
||||||
|
font-family: inherit;
|
||||||
|
color: #000;
|
||||||
|
border-radius: calc(var(--radius) * 2);
|
||||||
|
font-size: 1rem;
|
||||||
|
&::placeholder {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: var(--semi-medium);
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const UrlClosebtn = motion.div;
|
||||||
|
|
||||||
|
export const CloseBtn = styled.div`
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
right: 0.8rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 99;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Urlinput = ({ url, toggleurl, urlhandler, showurl, open }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AnimatePresence exitBeforeEnter={true}>
|
||||||
|
{showurl ? (
|
||||||
|
<UrlContainer>
|
||||||
|
<Url
|
||||||
|
key="urlbtn"
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
onBlur={toggleurl}
|
||||||
|
type="url"
|
||||||
|
name="url"
|
||||||
|
id="url"
|
||||||
|
placeholder="Paste your url"
|
||||||
|
pattern="https://.*"
|
||||||
|
autoComplete="off"
|
||||||
|
onChange={urlhandler}
|
||||||
|
value={url}
|
||||||
|
initial={{ width: 0 }}
|
||||||
|
animate={{ width: '220px' }}
|
||||||
|
exit={{
|
||||||
|
width: 0,
|
||||||
|
padding: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 1,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 1,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
type: 'spring',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<UrlClosebtn
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{
|
||||||
|
delay: 1,
|
||||||
|
}}>
|
||||||
|
<CloseBtn onClick={toggleurl}>
|
||||||
|
<IoMdClose color={'black'} size={20} />
|
||||||
|
</CloseBtn>
|
||||||
|
</UrlClosebtn>
|
||||||
|
</UrlContainer>
|
||||||
|
) : (
|
||||||
|
<Filebtn open={open} toggleurl={toggleurl} key1="btn1" key2="btn2" />
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Urlinput;
|
||||||
1
src/Components/Ui/images/Ei-share-apple.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" enable-background="new 0 0 50 50"><path d="M30.3 13.7L25 8.4l-5.3 5.3-1.4-1.4L25 5.6l6.7 6.7z"/><path d="M24 7h2v21h-2z"/><path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 361 B |
BIN
src/Components/Ui/images/chika.gif
Normal file
|
After Width: | Height: | Size: 471 KiB |
BIN
src/Components/Ui/images/kaguya-sama.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
49
src/Components/Ui/loader.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Loaderdiv = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Svg = styled.svg`
|
||||||
|
> path,
|
||||||
|
rect {
|
||||||
|
fill: var(--lavender);
|
||||||
|
/* opacity: 0.5; */
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Loader = () => {
|
||||||
|
return (
|
||||||
|
<Loaderdiv>
|
||||||
|
<Svg
|
||||||
|
version="1.1"
|
||||||
|
id="loader-1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
width="40px"
|
||||||
|
height="40px"
|
||||||
|
viewBox="0 0 50 50">
|
||||||
|
<path
|
||||||
|
fill="#000"
|
||||||
|
d="M43.935,25.145c0-10.318-8.364-18.683-18.683-18.683c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615c8.072,0,14.615,6.543,14.615,14.615H43.935z">
|
||||||
|
<animateTransform
|
||||||
|
attributeType="xml"
|
||||||
|
attributeName="transform"
|
||||||
|
type="rotate"
|
||||||
|
from="0 25 25"
|
||||||
|
to="360 25 25"
|
||||||
|
dur="0.6s"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
</Svg>
|
||||||
|
</Loaderdiv>
|
||||||
|
);
|
||||||
|
};
|
||||||
12
src/index.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import * as serviceworker from './serviceWorkerRegistration';
|
||||||
|
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
const container = document.getElementById('root');
|
||||||
|
const root = createRoot(container);
|
||||||
|
root.render(<App />);
|
||||||
|
serviceworker.register();
|
||||||
36
src/service-worker.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
import { clientsClaim } from 'workbox-core';
|
||||||
|
import { ExpirationPlugin } from 'workbox-expiration';
|
||||||
|
import { precacheAndRoute } from 'workbox-precaching';
|
||||||
|
import { registerRoute } from 'workbox-routing';
|
||||||
|
import { CacheFirst } from 'workbox-strategies';
|
||||||
|
import * as googleAnalytics from 'workbox-google-analytics';
|
||||||
|
|
||||||
|
clientsClaim();
|
||||||
|
googleAnalytics.initialize();
|
||||||
|
// Precache all of the assets generated by your build process.
|
||||||
|
// Their URLs are injected into the manifest variable below.
|
||||||
|
precacheAndRoute(self.__WB_MANIFEST);
|
||||||
|
|
||||||
|
// An example runtime caching route for requests that aren't handled by the
|
||||||
|
// precache, in this case same-origin .png requests like those from in public/
|
||||||
|
registerRoute(
|
||||||
|
// Add in any other file extensions or routing criteria as needed.
|
||||||
|
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst.
|
||||||
|
new CacheFirst({
|
||||||
|
cacheName: 'images',
|
||||||
|
plugins: [
|
||||||
|
// Ensure that once this runtime cache reaches a maximum size the
|
||||||
|
// least-recently used images are removed.
|
||||||
|
new ExpirationPlugin({ maxEntries: 10 }),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// This allows the web app to trigger skipWaiting via
|
||||||
|
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
|
||||||
|
self.addEventListener('message', event => {
|
||||||
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||||
|
self.skipWaiting();
|
||||||
|
}
|
||||||
|
});
|
||||||
132
src/serviceWorkerRegistration.js
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
// This optional code is used to register a service worker.
|
||||||
|
// register() is not called by default.
|
||||||
|
|
||||||
|
// This lets the app load faster on subsequent visits in production, and gives
|
||||||
|
// it offline capabilities. However, it also means that developers (and users)
|
||||||
|
// will only see deployed updates on subsequent visits to a page, after all the
|
||||||
|
// existing tabs open on the page have been closed, since previously cached
|
||||||
|
// resources are updated in the background.
|
||||||
|
|
||||||
|
// To learn more about the benefits of this model and instructions on how to
|
||||||
|
// opt-in, read https://cra.link/PWA
|
||||||
|
|
||||||
|
const isLocalhost = Boolean(
|
||||||
|
window.location.hostname === 'localhost' ||
|
||||||
|
// [::1] is the IPv6 localhost address.
|
||||||
|
window.location.hostname === '[::1]' ||
|
||||||
|
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||||
|
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
|
||||||
|
);
|
||||||
|
|
||||||
|
export function register(config) {
|
||||||
|
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||||
|
// The URL constructor is available in all browsers that support SW.
|
||||||
|
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||||
|
if (publicUrl.origin !== window.location.origin) {
|
||||||
|
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||||
|
// from what our page is served on. This might happen if a CDN is used to
|
||||||
|
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||||
|
|
||||||
|
if (isLocalhost) {
|
||||||
|
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||||
|
checkValidServiceWorker(swUrl, config);
|
||||||
|
|
||||||
|
// Add some additional logging to localhost, pointing developers to the
|
||||||
|
// service worker/PWA documentation.
|
||||||
|
navigator.serviceWorker.ready.then(() => {
|
||||||
|
console.log('This web app is being served cache-first by a service worker.');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Is not localhost. Just register service worker
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerValidSW(swUrl, config) {
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register(swUrl)
|
||||||
|
.then(registration => {
|
||||||
|
registration.onupdatefound = () => {
|
||||||
|
const installingWorker = registration.installing;
|
||||||
|
if (installingWorker == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
installingWorker.onstatechange = () => {
|
||||||
|
if (installingWorker.state === 'installed') {
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
// At this point, the updated precached content has been fetched,
|
||||||
|
// but the previous service worker will still serve the older
|
||||||
|
// content until all client tabs are closed.
|
||||||
|
console.log(
|
||||||
|
'New content is available and will be used when all tabs for this page are closed'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Execute callback
|
||||||
|
if (config && config.onUpdate) {
|
||||||
|
config.onUpdate(registration);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// At this point, everything has been precached.
|
||||||
|
// It's the perfect time to display a
|
||||||
|
// "Content is cached for offline use." message.
|
||||||
|
console.log('Content is cached for offline use.');
|
||||||
|
|
||||||
|
// Execute callback
|
||||||
|
if (config && config.onSuccess) {
|
||||||
|
config.onSuccess(registration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error during service worker registration:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkValidServiceWorker(swUrl, config) {
|
||||||
|
// Check if the service worker can be found. If it can't reload the page.
|
||||||
|
fetch(swUrl, {
|
||||||
|
headers: { 'Service-Worker': 'script' },
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
// Ensure service worker exists, and that we really are getting a JS file.
|
||||||
|
const contentType = response.headers.get('content-type');
|
||||||
|
if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {
|
||||||
|
// No service worker found. Probably a different app. Reload the page.
|
||||||
|
navigator.serviceWorker.ready.then(registration => {
|
||||||
|
registration.unregister().then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Service worker found. Proceed as normal.
|
||||||
|
registerValidSW(swUrl, config);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log('No internet connection found. App is running in offline mode.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unregister() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.ready
|
||||||
|
.then(registration => {
|
||||||
|
registration.unregister();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/store/Context-Provider.js
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import { createContext, useState } from 'react';
|
||||||
|
import { instance, TRACE_MOE_QUERY, ANILIST_QUERY, query } from '../Api/constant';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const defaultstate = {
|
||||||
|
imagehandler: () => {},
|
||||||
|
image: null,
|
||||||
|
urlhandler: () => {},
|
||||||
|
url: null,
|
||||||
|
fileUpload: () => {},
|
||||||
|
video: null,
|
||||||
|
loading: false,
|
||||||
|
animeinfo: null,
|
||||||
|
animeinfoexits: false,
|
||||||
|
cardhandler: () => {},
|
||||||
|
servererror: false,
|
||||||
|
usererror: false,
|
||||||
|
errorhandler: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Context = createContext(defaultstate);
|
||||||
|
|
||||||
|
export const ContextProvider = props => {
|
||||||
|
const [image, setimage] = useState(null);
|
||||||
|
const [url, seturl] = useState('');
|
||||||
|
const [video, setvideo] = useState(null);
|
||||||
|
const [loading, setloading] = useState(false);
|
||||||
|
const [animeinfoexits, setanimeinfoexits] = useState(false);
|
||||||
|
const [animeinfo, setanimeinfo] = useState({});
|
||||||
|
const [servererror, setServerError] = useState(false);
|
||||||
|
const [usererror, setUserError] = useState(false);
|
||||||
|
|
||||||
|
// This is used to Change states to default when new image or url is used
|
||||||
|
const Changestates = () => {
|
||||||
|
seturl('');
|
||||||
|
setimage(null);
|
||||||
|
setloading(false);
|
||||||
|
setvideo(null);
|
||||||
|
setanimeinfo({});
|
||||||
|
setanimeinfoexits(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function is used to Remove the Result card
|
||||||
|
const cardhandler = () => {
|
||||||
|
setanimeinfoexits(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function is used to Remove the Error card
|
||||||
|
const errorhandler = () => {
|
||||||
|
setServerError(false);
|
||||||
|
setUserError(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fechanimeinfo = async (anilistid, episode, from, similarity) => {
|
||||||
|
var variables = {
|
||||||
|
id: anilistid,
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
query: query,
|
||||||
|
variables: variables,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { data } = await instance.post(ANILIST_QUERY, body);
|
||||||
|
setanimeinfo({
|
||||||
|
episode: episode,
|
||||||
|
time: from,
|
||||||
|
similarity: similarity,
|
||||||
|
...data.data.Media,
|
||||||
|
});
|
||||||
|
setanimeinfoexits(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the image when image is selected
|
||||||
|
const imagehandler = async acceptedfile => {
|
||||||
|
Changestates();
|
||||||
|
const file = acceptedfile[0];
|
||||||
|
if (file && file.type.substr(0, 5) === 'image') return setimage(file);
|
||||||
|
return setimage(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get the url when url is used
|
||||||
|
const urlhandler = async e => {
|
||||||
|
Changestates();
|
||||||
|
const url = e.target.value;
|
||||||
|
if (url) return seturl(url);
|
||||||
|
return seturl('');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get data from the trace.moe API and calls Anilist APi to get animeinfo
|
||||||
|
const getdata = data => {
|
||||||
|
const { anilist, video, episode, from, similarity } = data.result[0];
|
||||||
|
setvideo(video);
|
||||||
|
setloading(false);
|
||||||
|
fechanimeinfo(anilist, episode, from, similarity);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fileUpload = async e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setloading(false);
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.set('image', image);
|
||||||
|
const body = formData;
|
||||||
|
if (image || url) {
|
||||||
|
setloading(true);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (url) {
|
||||||
|
const { data } = await instance.get(`?url=${encodeURIComponent(url)}`);
|
||||||
|
getdata(data);
|
||||||
|
} else if (image) {
|
||||||
|
const { data } = await instance.post(TRACE_MOE_QUERY, body);
|
||||||
|
getdata(data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setloading(false);
|
||||||
|
|
||||||
|
if (error.response) {
|
||||||
|
const status = Array.from(String(error.response.status), Number);
|
||||||
|
if (status[0] === 4) return setUserError(true);
|
||||||
|
console.log('Something went wrong in the backend', error);
|
||||||
|
return setServerError(true);
|
||||||
|
}
|
||||||
|
if (error.request) {
|
||||||
|
console.log('Due to network issue or image not provided', error);
|
||||||
|
return setUserError(true);
|
||||||
|
}
|
||||||
|
return console.log('something else happened', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createContext = {
|
||||||
|
imagehandler,
|
||||||
|
image,
|
||||||
|
urlhandler,
|
||||||
|
url,
|
||||||
|
fileUpload,
|
||||||
|
loading,
|
||||||
|
video,
|
||||||
|
animeinfo,
|
||||||
|
animeinfoexits,
|
||||||
|
cardhandler,
|
||||||
|
servererror,
|
||||||
|
usererror,
|
||||||
|
errorhandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Context.Provider value={createContext}>{props.children}</Context.Provider>;
|
||||||
|
};
|
||||||
90
src/styles/GlobalStyle.js
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import { createGlobalStyle } from 'styled-components';
|
||||||
|
|
||||||
|
export const GlobalStyle = createGlobalStyle`
|
||||||
|
:root {
|
||||||
|
/* Colors */
|
||||||
|
--lavender: #d9d9f9;
|
||||||
|
--lavenderlight: #f0f3ff;
|
||||||
|
--lightblue: #2b2bde;
|
||||||
|
--link: #0019f6;
|
||||||
|
--nav: #fff;
|
||||||
|
--cardbg: #151515;
|
||||||
|
--Styledlinks: #394ae4;
|
||||||
|
|
||||||
|
--border: 1.5px solid #fff;
|
||||||
|
--radius: 0.9rem;
|
||||||
|
--card-radius: 15px 15px 0px 0px;
|
||||||
|
--box-shadow: 0px 5px 29px -4px rgba(0, 0, 0, 0.25), inset 0px 1px 5px rgba(0, 0, 0, 0.25);
|
||||||
|
|
||||||
|
/* Font Weights */
|
||||||
|
--regular: 400;
|
||||||
|
--semi-medium: 500;
|
||||||
|
--medium: 600;
|
||||||
|
--semi-bold: 700;
|
||||||
|
--bold: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
color: #fff;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background: linear-gradient(116.2deg, #d9e5fa -0.48%, #fad9f3 102.36%);
|
||||||
|
text-size-adjust: none;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
-moz-text-size-adjust: none;
|
||||||
|
-ms-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
div,
|
||||||
|
p {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
/* display: inline-block; */
|
||||||
|
list-style: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
30
src/styles/mixins.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/** @format */
|
||||||
|
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { css } from 'styled-components';
|
||||||
|
|
||||||
|
export const Imagecontainer = styled.div`
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const breakpoints = {
|
||||||
|
xs: '450px',
|
||||||
|
sm: '550px',
|
||||||
|
tab: '660px',
|
||||||
|
md: '750px',
|
||||||
|
lg: '900px',
|
||||||
|
xl: '1100px',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const respondTo = Object.keys(breakpoints).reduce(
|
||||||
|
(accumulator, label) => {
|
||||||
|
accumulator[label] = (...args) => css`
|
||||||
|
@media (min-width: ${breakpoints[label]}) {
|
||||||
|
${css(...args)};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
return accumulator;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||