Compare commits

..

1 Commits

Author SHA1 Message Date
Ota Prokopec 5d5f7f5ef4 routes 2022-12-18 18:00:19 +01:00
149 changed files with 5643 additions and 4719 deletions

View File

@ -9,6 +9,11 @@ charset = utf-8
trim_trailing_whitespace = false trim_trailing_whitespace = false
quote_type = single quote_type = single
[*.{yml, md}] [*.yml]
end_of_line = lf
insert_final_newline = true
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
charset = utf-8
trim_trailing_whitespace = false
quote_type = single

27
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,27 @@
on:
push:
branches: [ "deploy/prod" ]
pull_request:
branches: [ "deploy/prod" ]
name: 🚀 Deploy website on push
jobs:
web-deploy:
name: 🎉 Deploy
runs-on: ubuntu-latest
steps:
- name: 🚚 Get latest code
uses: actions/checkout@v2
- name: 🔨 Build Project
run: |
npm ci
npm run build
- name: 📂 Sync files
uses: SamKirkland/FTP-Deploy-Action@4.3.2
with:
server: 305244.w44.wedos.net
username: w305244_BOT007
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./dist/

4
.gitignore vendored
View File

@ -23,5 +23,5 @@ dist-ssr
*.sln *.sln
*.sw? *.sw?
# appwrite # php
appwrite public/api/vendor

233
README.md
View File

@ -1,229 +1,10 @@
# Svelte + Appwrite = 🚀 # Geohry
## Appwrite svelte template Vzdělávejte se mimo lavice. Nechte se provést atraktivními lokacemi, vyřešte mnoho výzev a objevte nejednu geografickou zajímavost s bezplatnou mobilní aplikací.
Blazing fast development with done backend and fully-prepared frontend. 1. Hrací režim
2. QR kódy
3. Nacházení blízkých her do x km a jejich seznam
4. Přihlašování/registrace
CMS ready! https://www.figma.com/file/mEaF6SbE8aP1WCo95f8dlV/Erant
## Appwrite installation
[Appwrite installation](https://appwrite.io/docs/installation)
## Frontend included
* tailwind
* scss
* css reset
* typescript
* routing
* ready routes
* oauth
* files upload, download
* folder structure
* common components
* service worker
* path aliases
* database realtime subscribers
* database paginate, infinity scroll
* i18n
* cms
* cms forms components
* vite
* prettier
* editorconfig
* icons: [Bootstrap icons](https://icons.getbootstrap.com/)
## Database subscribers
```svelte
<script>
import { Collection } from '$lib/database'
import { Query } from 'appwrite'
const collection = new Collection('[database-id]', '[collection-id]')
const [subscriber, loading] = collection.createSubscriber([Query.limit(5) /*, ...queries */])
// listen changes (update, delete) in database and automatically rerender on change
// current data = [{ name: 'John', lastName: 'Doe' }, ...]
const insertSubscriber = collection.createObserver()
// listen changes (create) in database and automatically rerender on change
const [paginator, paginatorInitalLoading] = collection.createPaginate(10, [/* ...queries */])
// paginate the collection of documents with limit and automatically rerender on change
// paginator.next() makes the next request for items, paginator store automatically rerender on next load
const [scrollData, scrollDispatch] = collection.createInfinityScrollDispatcher(10, [/* ...queries */], { /* intersection observer options */ })
// load next data after scroll to anchor (scrollDispatch) element
</script>
<div>
{#if $loading}
<p>Loading data from database...</p>
{:else}
{#each [...$subscriber, ...$insertSubscriber] as item}
<p>{item.name}</p>
{/each}
{/if}
</div>
<!-- scroll dispatcher example -->
<div>
{#each $scrollData as item}
<p>{item.name}</p>
{/each}
<div use:scrollDispatch on:fetch={(e) => console.log(e) /* on every fetch from scroll dispatcher do some action */} />
</div>
```
## Files subscribers
```svelte
<script>
import { Bucket } from '$lib/storage'
import { Query } from 'appwrite'
const bucket = new Bucket('[bucket-id]')
const [files, loading] = bucket.createSubscriber([Query.limit(5) /*, ...queries */])
// listen changes (update, delete) in files and automatically rerender on change
const insertSubscriber = bucket.createObserver()
// listen changes (create) in files and automatically rerender on change
const [upload, dispatch] = storage.createUploadDispatcher(/* many files ? true : false, default = false */)
const [content, loading] = storage.getFileContent('6391f7c70ede82115575')
// get file content and automatically rerender on file update
</script>
<div>
<input type="file" use:upload />
<button on:click={() => dispatch().then(uploadedFile => console.log(uploadedFile))}>Upload</button>
</div>
```
## Routing
Routes can be added in `__routes.svelte` file. Every route is fetched lazyly.
```svelte
<script lang="ts">
import Router from '$lib/router/Router.svelte'
import Layout from '$src/__layout.svelte'
import Loading from '$src/__loading.svelte'
import Error from '$src/__error.svelte'
</script>
<Router
layout={Layout}
loading={Loading}
error={Error}
routes={[
{
path: '/',
component: () => import('$routes/index.svelte'),
},
{
path: '/oauth',
component: () => import('$routes/oauth/index.svelte'),
},
{
path: '/oauth/failure',
component: () => import('$routes/oauth/failure.svelte'),
},
{
path: '/oauth/success',
component: () => import('$routes/oauth/success.svelte'),
},
]}
/>
```
### Routes structure
`__layout.svelte` the default layout for every page
`__error.svelte` the error page (404 error)
`__loading.svelte` the default loading component
`__routes.svelte` the file includes all routes in application
## Social auth
```svelte
<script>
import { account, url } from '$lib/stores/appwrite'
</script>
<div>
<button on:click={() => account.createOAuth2Session('github', url.oauth.success, url.oauth.failure)}>
Github
</button>
</div>
```
## i18n
Locale file `src/locales/en.json`
```json
{
"page": {
"home": {
"title": "Appwrite svelte rocket start 🚀"
}
}
}
```
```svelte
<script>
import { _, locale, locales } from 'svelte-i18n'
</script>
<div>
<h1>{$_('page.home.title')}</h1>
<div>
<p>Change language:</p>
<select bind:value={$locale}>
{#each $locales as locale}
<option value={locale}>{locale}</option>
{/each}
</select>
</div>
</div>
```
## path aliases
`$lib` = `src/lib`
`$root` = `/`
`$src` = `src`
`$cms` = `src/cms`
`$routes` = `src/routes`
## commands
```bash
npm run dev
```
```bash
npm run build
```
```bash
npm run preview
```
```bash
npm run appwrite
```

9
composer.json Normal file
View File

@ -0,0 +1,9 @@
{
"config": {
"vendor-dir": "public/api/vendor"
},
"require": {
"bramus/router": "1.5",
"catfan/medoo": "^2.1"
}
}

146
composer.lock generated Normal file
View File

@ -0,0 +1,146 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "df49ce8601cf8f4f5d004e144d8ecaa5",
"packages": [
{
"name": "bramus/router",
"version": "1.5",
"source": {
"type": "git",
"url": "https://github.com/bramus/router.git",
"reference": "6adc0182dc6b0ebc22fd10543f65a36de10d8c05"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bramus/router/zipball/6adc0182dc6b0ebc22fd10543f65a36de10d8c05",
"reference": "6adc0182dc6b0ebc22fd10543f65a36de10d8c05",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2.14",
"phpunit/php-code-coverage": "~2.0",
"phpunit/phpunit": "~4.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Bramus": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bram(us) Van Damme",
"email": "bramus@bram.us",
"homepage": "http://www.bram.us"
}
],
"description": "A lightweight and simple object oriented PHP Router",
"homepage": "https://github.com/bramus/router",
"keywords": [
"router",
"routing"
],
"support": {
"issues": "https://github.com/bramus/router/issues",
"source": "https://github.com/bramus/router/tree/1.5"
},
"time": "2020-10-26T09:50:48+00:00"
},
{
"name": "catfan/medoo",
"version": "v2.1.7",
"source": {
"type": "git",
"url": "https://github.com/catfan/Medoo.git",
"reference": "74be58f80d330d1075ef3bfd0fb3d057349597ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/catfan/Medoo/zipball/74be58f80d330d1075ef3bfd0fb3d057349597ae",
"reference": "74be58f80d330d1075ef3bfd0fb3d057349597ae",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
},
"suggest": {
"ext-pdo_dblib": "For MSSQL or Sybase database on Linux/UNIX platform",
"ext-pdo_mysql": "For MySQL or MariaDB database",
"ext-pdo_oci": "For Oracle database",
"ext-pdo_pqsql": "For PostgreSQL database",
"ext-pdo_sqlite": "For SQLite database",
"ext-pdo_sqlsrv": "For MSSQL database on both Window/Liunx platform"
},
"type": "framework",
"autoload": {
"psr-4": {
"Medoo\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Angel Lai",
"email": "angel@medoo.in"
}
],
"description": "The lightweight PHP database framework to accelerate development",
"homepage": "https://medoo.in",
"keywords": [
"database",
"database library",
"lightweight",
"mariadb",
"mssql",
"mysql",
"oracle",
"php framework",
"postgresql",
"sql",
"sqlite"
],
"support": {
"issues": "https://github.com/catfan/Medoo/issues",
"source": "https://github.com/catfan/Medoo"
},
"funding": [
{
"url": "https://paypal.me/AngelaonLai",
"type": "custom"
},
{
"url": "https://opencollective.com/medoo",
"type": "open_collective"
}
],
"time": "2022-07-02T04:27:50+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.1.0"
}

View File

@ -3,12 +3,42 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/webmanifest.json" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Svelte + Appwrite</title> <title>Erant</title>
<!-- Meta Pixel Code -->
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '541491674547104');
fbq('track', 'PageView');
</script>
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=541491674547104&ev=PageView&noscript=1"
/></noscript>
<!-- End Meta Pixel Code -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XJMWVPP9DC"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XJMWVPP9DC');
</script>
</head> </head>
<body> <body>
<noscript>Tato stránka potřebuje Javascript.</noscript>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>

View File

@ -1,8 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"moduleResolution": "Node", "moduleResolution": "node",
"target": "ESNext", "target": "esnext",
"module": "ESNext", "module": "esnext",
/** /**
* svelte-preprocess cannot figure out whether you have * svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using * a value or a type, so tell TypeScript to enforce using
@ -19,6 +19,7 @@
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"baseUrl": ".",
/** /**
* Typecheck JS in `.svelte` and `.js` files by default. * Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types. * Disable this if you'd like to use dynamic types.

4634
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"name": "appwrite-svelte-rocket-start", "name": "geohry",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
@ -7,25 +7,25 @@
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"appwrite": "docker compose -f ./appwrite/docker-compose.yml up" "php": "php -S localhost:8000 -t ./public"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.1.0", "@sveltejs/vite-plugin-svelte": "^1.0.1",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.8",
"flowbite-svelte": "^0.28.4", "postcss": "^8.4.16",
"postcss": "^8.4.19", "postcss-load-config": "^4.0.1",
"sass": "^1.56.1", "sass": "^1.53.0",
"svelte": "^3.52.0", "svelte": "^3.49.0",
"svelte-preprocess": "^4.10.7", "svelte-preprocess": "^4.10.7",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.1.8",
"typescript": "^4.9.3", "vite": "^3.1.6"
"vite": "^3.2.3"
}, },
"dependencies": { "dependencies": {
"@bytemd/plugin-gfm": "^1.17.4", "@beyonk/svelte-mapbox": "^8.1.4",
"appwrite": "^10.1.0", "html5-qrcode": "^2.3.0",
"bytemd": "^1.17.4", "mapbox": "^1.0.0-beta10",
"svelte-i18n": "^3.6.0", "mapbox-gl": "^2.10.0",
"svelte-routing": "^1.6.0" "svelte-routing": "^1.6.0",
"yallist": "^4.0.0"
} }
} }

View File

@ -1,6 +1,6 @@
const autoprefixer = require('autoprefixer')
const tailwind = require('tailwindcss')
module.exports = { module.exports = {
plugins: { plugins: [tailwind(), autoprefixer()],
tailwindcss: {},
autoprefixer: {},
},
} }

8
public/.htaccess Normal file
View File

@ -0,0 +1,8 @@
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]

5
public/api/.htaccess Normal file
View File

@ -0,0 +1,5 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]

37
public/api/cors.php Normal file
View File

@ -0,0 +1,37 @@
<?php
/**
* An example CORS-compliant method. It will allow any GET, POST, or OPTIONS requests from any
* origin.
*
* In a production environment, you probably want to be more restrictive, but this gives you
* the general idea of what is involved. For the nitty-gritty low-down, read:
*
* - https://developer.mozilla.org/en/HTTP_access_control
* - https://fetch.spec.whatwg.org/#http-cors-protocol
*
*/
function cors() {
// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
// Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
// you want to allow, and if so:
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Header: *");
header("Access-Control-Max-Age: 86400"); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
header("Access-Control-Allow-Origin: *");
exit(0);
}
}

41
public/api/index.php Normal file
View File

@ -0,0 +1,41 @@
<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/cors.php';
cors();
$router = new \Bramus\Router\Router();
$router->setBasePath('/api');
use Medoo\Medoo;
header('Content-Type: application/json; charset=utf-8');
function env($key) {
global $_ENV;
return array_key_exists($key, $_ENV) ? $_ENV[$key] : null;
}
$database = new Medoo([
'type' => 'mysql',
'host' => "db.db030.webglobe.com",
'database' => "mysql122279",
'username' => "mysql85033",
'password' => "troglodyt"
]);
$data = json_decode(file_get_contents("php://input"), true);
$router->post('/game/details', function() use($database, $data) {
$fn = require __DIR__ . '/routes/game/details.php';
$response = $fn($data, getallheaders(), $database);
$status = array_key_exists("status", $response) ? $response["status"] : 200;
unset($response['status']);
http_response_code($status);
echo json_encode($response);
});
$router->run();

View File

@ -0,0 +1,70 @@
<?php
function remoteFileExists($url) {
$curl = curl_init($url);
//don't fetch the actual page, you only want to check the connection is ok
curl_setopt($curl, CURLOPT_NOBODY, true);
//do request
$result = curl_exec($curl);
$ret = false;
//if request did not fail
if ($result !== false) {
//if request was ok, check response code
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($statusCode == 200) {
$ret = true;
}
}
curl_close($curl);
return $ret;
}
return function($data, $headers, $db) {
$imagesHostName = 'https://geohry.skolazdola.cz/';
if(!isset($data['gameurl']) || empty($data['gameurl'])) return [
"success" => false,
"data" => [
"message" => "Property 'gameurl' is not set."
]
];
$gameUrl = $data['gameurl'];
$gameDetails = $db->select('games4', '*', [
'url' => $gameUrl
]);
if(!$gameDetails) return [
"success" => false,
"data" => [
"message" => "Invalid 'gameurl'."
]
];
$gameDetails = $gameDetails[0];
$gameDetails["questions"] = $db->select('questions4', '*', [
'url' => $gameUrl
]);
$thumbnailPath = "{$imagesHostName}/games/{$gameUrl}/intro.jpg";
$gameDetails["thumbnail"] = remoteFileExists($thumbnailPath) ? $thumbnailPath : null;
foreach($gameDetails["questions"] as &$question) {
$questionThumbnailPath = "{$imagesHostName}/games/{$gameUrl}/" . $question["uniqid"] . ".jpg";
$question["thumbnail"] = remoteFileExists($questionThumbnailPath) ? $questionThumbnailPath : null;
}
return [
"success" => true,
"data" => $gameDetails
];
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,93 @@
Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name Source.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,12 +0,0 @@
server {
server_name cms.example.com;
location / {
proxy_pass http://127.0.0.1:xxxx;
}
}
server {
server_name *.example.com;
location / {
proxy_pass http://127.0.0.1:xxxx;
}
}

View File

@ -2,3 +2,4 @@
self.addEventListener("install", (e) => { }) self.addEventListener("install", (e) => { })
self.addEventListener('fetch', (e) => { }) self.addEventListener('fetch', (e) => { })

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

19
public/webmanifest.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "Erant",
"short_name": "Erant",
"icons": [
{
"src": "assets/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

10
src/fonts.scss Normal file
View File

@ -0,0 +1,10 @@
@font-face {
font-family: "Source Sans Pro";
src: url("/assets/fonts/SourceSansPro-Regular.ttf") format("ttf");
}
@font-face {
font-family: "Source Sans Pro";
font-style: italic;
src: url("/assets/fonts/SourceSansPro-Italic.ttf") format("ttf");
}

View File

@ -0,0 +1,53 @@
<script>
import { navigate } from 'svelte-routing'
export let href = ''
import { createEventDispatcher } from 'svelte'
const dispatch = createEventDispatcher()
export let primary = false
export let style = ''
let className = ''
export { className as class }
</script>
<button {style} on:click={() => navigate(href)} on:click={() => dispatch('submit', true)} on:click class={className} class:primary>
<slot />
</button>
<style lang="scss">
button {
border-radius: 25px;
cursor: pointer;
display: inline-block;
font-size: 30px;
font-weight: 600;
line-height: 20px;
padding: 20px;
text-align: center;
outline: none;
box-shadow: none;
border: none;
transition: background-color 0.3s;
background-color: rgb(232, 232, 232);
color: #4263eb;
word-break: break-word;
overflow: auto;
&.primary {
color: white;
background-color: rgb(66, 99, 235);
&:hover {
background-color: rgba(66, 99, 235, 0.8);
}
}
&:hover {
background-color: rgb(232, 232, 232, 0.8);
}
}
</style>

View File

@ -0,0 +1,55 @@
<script>
export let shareData
const share = async () => {
if ('share' in window.navigator && navigator.canShare(shareData)) {
await navigator.share(shareData)
} else {
navigator.clipboard.writeText(shareData.url)
}
}
</script>
<button on:click={share}>
<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g filter="url(#filter0_i_956_444)">
<rect width="42" height="42" fill="url(#share_svelteSvgPictureid)" />
</g>
<defs>
<filter id="filter0_i_956_444" x="0" y="0" width="42" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<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 dx="436" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0.258824 0 0 0 0 0.388235 0 0 0 0 0.921569 0 0 0 1 0" />
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_956_444" />
</filter>
<pattern id="share_svelteSvgPictureid" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_956_444" transform="scale(0.00520833)" />
</pattern>
<image
id="image0_956_444"
width="192"
height="192"
xlink:href="data:image/png;base64,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"
/>
</defs>
</svg>
</button>
<style lang="scss">
button {
cursor: pointer;
background: #e8e8e8;
border-radius: 16px;
z-index: 6;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
border: none;
outline: none;
}
</style>

View File

@ -0,0 +1,61 @@
<script>
import Times from './../../svg/Times.svelte'
import Setting from './../../svg/Setting.svelte'
import { navigate } from 'svelte-routing'
import { onMount } from 'svelte'
let showCookiePopUp = localStorage.getItem('cookies') !== 'dismissed'
let animation = false
let url = window.location.href
onMount(() => {
if (url.replace(window.location.origin, "") === "/"){
if (localStorage.getItem("ErantFirstTime") == null){
animation = true
setTimeout(() => {
animation = false
localStorage.setItem('ErantFirstTime', "false");
}, 5000)
}
}
})
</script>
{#if showCookiePopUp && !animation}
<div class="cookies">
<div
class="shadow w-[calc(var(--max-viewport-width))] max-w-[98%] h-auto rounded-[6px] overflow-hidden absolute bottom-0 bg-white gap-4 z-50 mb-[10px] flex flex-wrap flex-row justify-self-center"
>
<div class="w-full h-min grid grid-cols-[min-content_auto_min-content] gap-4 p-[16px]">
<button on:click={() => navigate('cookies/settings')} class="w-min h-min flex-grow-0"><Setting /></button>
<div class="flex flex-wrap flex-row gap-2">
<div class="text-[28px] font-semibold">Cookies settings</div>
<div>Our website uses cookies to operate, by continuing you agree to our <a class="text-[#4263EB]" href="/cookies/policy">cookie policy</a>.</div>
</div>
<button on:click={() => (showCookiePopUp = false)} class="w-min h-min relative flex-grow-0"><Times /></button>
</div>
<div class="w-full h-auto flex justify-end bg-[#F3F4F6] p-[16px]">
<button
on:click={() => {
showCookiePopUp = false
localStorage.setItem('cookies', 'dismissed')
}}
class="w-[84px] h-[37px] bg-[#09205D] text-white rounded-[8px] text-[18px]">Dismiss</button
>
</div>
</div>
</div>
{/if}
<style lang="scss">
.cookies{
width: 100%;
height: 100vh;
position: absolute;
z-index: 9999;
backdrop-filter: blur(5px) brightness(60%);
}
.shadow {
box-shadow: 0 0 1px;
left: 50%;
transform: translate(-50%, 0);
}
</style>

View File

@ -0,0 +1,39 @@
<script>
import { idGenerator } from '$lib/utils/IdGenerator'
const id = idGenerator()
export let checked = true
</script>
<div class="cover h-14">
<div class="h-14 w-14 flex justify-center items-center">
<input
{id}
bind:checked
type="checkbox"
class="relative w-[3.25rem] h-7 bg-gray-100 checked:bg-none checked:bg-[#96ddff] border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 border border-transparent ring-1 ring-transparent focus:border-[#96ddff] focus:ring-[#96ddff] ring-offset-white focus:outline-none appearance-none before:inline-block before:w-6 before:h-6 before:bg-white checked:before:bg-[var(--blue)] before:translate-x-0 checked:before:translate-x-full before:shadow before:rounded-full before:transform before:ring-0 before:transition before:ease-in-out before:duration-20"
/>
</div>
<label for={id}><slot /></label>
</div>
<style>
.cover {
height: 100%;
width: fit-content;
background: white;
display: grid;
grid-template-columns: 56px auto;
gap: 20px;
padding: 4px;
}
label {
font-size: 100%;
width: 100%;
height: 100%;
display: flex;
align-items: center;
font-size: 140%;
}
</style>

View File

@ -0,0 +1,65 @@
<script>
import Eye from '../../svg/Eye.svelte'
export let name = ''
export let placeholder = ''
let input
let vissible = false
function changeVisibility(i) {
if (!vissible) {
i.type = 'text'
vissible = true
} else {
i.type = 'password'
vissible = false
}
}
</script>
<div>
<input bind:this={input} type="password" {name} {placeholder} minlength="8" class="input" required />
<button type="button" on:click={changeVisibility(input)}>
<Eye active={vissible} />
</button>
</div>
<style lang="scss">
div {
width: 100%;
position: relative;
button {
position: absolute;
right: 25px;
top: 50%;
transform: translateY(-50%);
}
button:hover {
opacity: 50%;
}
}
.input {
width: calc(100%);
padding: 12px 24px;
border-radius: 15px;
background-color: #eeeeee;
font-size: 18px;
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
line-height: 28px;
@media screen and (max-height: 720px) {
padding: 8px 24px;
}
}
::placeholder {
color: #8f8f8f;
font-size: 18px;
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
line-height: 28px;
}
</style>

View File

@ -0,0 +1,31 @@
<script>
export let value = ''
export let type = 'text'
export let placeholder = ''
let className
export { className as class }
function typeAction(node) {
node.type = type
}
</script>
<input bind:value class="input {className}" use:typeAction {placeholder} on:input />
<style>
input {
background-color: rgb(232, 232, 232);
color: var(--blue);
background: white;
border: 4px solid var(--blue);
border-radius: 25px;
text-align: left;
font-size: 30px;
font-weight: 600;
line-height: 20px;
padding: 28px 20px;
outline: none;
appearance: none;
}
</style>

View File

@ -0,0 +1,7 @@
<script>
import InputPicture from '$lib/svg/InputPicture.svelte'
</script>
<div>
<InputPicture />
</div>

View File

@ -0,0 +1,52 @@
<script>
import { idGenerator } from '../../utils/IdGenerator'
const id = idGenerator()
export let group = false
export let value
</script>
<div class="cover h-14 cursor-pointer">
<div class="h-14 w-14 flex justify-center items-center">
<input {id} bind:group {value} type="radio" class="w-6 h-6 absolute flex justify-center items-center text-red-600 bg-gray-100 border-gray-300" />
</div>
<label for={id}><slot /></label>
</div>
<style lang="scss">
.cover {
height: 100%;
width: fit-content;
background: white;
display: grid;
grid-template-columns: 56px auto;
gap: 20px;
padding: 4px;
label {
font-size: 100%;
width: 100%;
height: 100%;
display: flex;
align-items: center;
font-size: 140%;
}
input:checked {
appearance: none;
background-color: var(--blue);
border-radius: 100%;
border: 2px solid black;
}
input:checked::before {
content: '';
height: 40%;
width: 40%;
background: black;
position: absolute;
border-radius: 100%;
}
}
/*border: 4px solid var(--blue);
box-shadow: 0 0 4px var(--blue), 0 0 1px var(--blue) inset;
border-radius: var(--border-radius);*/
</style>

View File

@ -0,0 +1,33 @@
<script>
</script>
<div class="cover h-14">
<div class="h-14 w-14 flex justify-center items-center">
<input type="range" min="1" max="11" step="2" />
</div>
<span><slot /></span>
</div>
<style lang="scss">
.cover {
height: 100%;
width: fit-content;
background: white;
display: grid;
grid-template-columns: 56px auto;
gap: 20px;
padding: 4px;
span {
font-size: 100%;
width: 100%;
height: 100%;
display: flex;
align-items: center;
font-size: 140%;
}
}
/*border: 4px solid var(--blue);
box-shadow: 0 0 4px var(--blue), 0 0 1px var(--blue) inset;
border-radius: var(--border-radius);*/
</style>

View File

@ -0,0 +1,72 @@
<script>
import Sharebutton from '../Buttons/Sharebutton.svelte'
export let img = null
export let shareData = null
export let alt = 'background image'
</script>
<div class="h-full w-full overflow-auto">
<section class="wraper">
{#if img}
<img src={img} {alt} />
{/if}
{#if shareData !== null && shareData !== false}
<div class="shareButton">
<Sharebutton {shareData} />
</div>
{/if}
<section class="overlay">
<slot />
</section>
</section>
</div>
<style lang="scss">
.wraper {
position: relative;
height: 100%;
width: 100%;
clip-path: inset(0);
overflow: auto;
.overlay {
min-height: 75vh;
height: auto;
width: 100%;
overflow-y: auto;
position: absolute;
top: 25%;
border-radius: 70px 70px 0 0;
padding: 52px;
display: flex;
flex-direction: column;
row-gap: 35px;
z-index: 7;
background-color: white;
}
img {
z-index: 4;
width: 100%;
height: auto;
position: absolute;
}
.shareButton {
width: 100%;
height: calc(100% - 75%);
display: flex;
justify-content: flex-end;
align-items: center;
position: absolute;
top: 0;
padding: 50px;
background: none;
clip-path: inset(0);
z-index: 6;
@media (min-width: 768px) {
position: absolute;
}
}
}
</style>

View File

@ -0,0 +1,86 @@
<script>
import { navigate } from 'svelte-routing'
import FooterItem from '../common/NavBar_Item.svelte'
let items = [
{
name: 'Home',
url: '/',
image_url:
"data:image/svg+xml,%3Csvg width='23' height='24' viewBox='0 0 23 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8.1572 22.2352V18.6679C8.1572 17.7573 8.90083 17.019 9.81813 17.019H13.1713C13.6118 17.019 14.0343 17.1928 14.3458 17.502C14.6573 17.8112 14.8323 18.2306 14.8323 18.6679V22.2352C14.8295 22.6138 14.979 22.9779 15.2477 23.2465C15.5164 23.5152 15.882 23.6663 16.2634 23.6663H18.5511C19.6195 23.6691 20.6452 23.2497 21.4017 22.5006C22.1581 21.7516 22.5833 20.7345 22.5833 19.6738V9.51101C22.5833 8.65421 22.2007 7.84149 21.5387 7.29179L13.7563 1.12152C12.4026 0.0396631 10.4629 0.0745936 9.14958 1.20448L1.54481 7.29179C0.85149 7.82529 0.437104 8.64042 0.416626 9.51101V19.6634C0.416626 21.8742 2.2219 23.6663 4.44883 23.6663H6.6843C7.47639 23.6663 8.12012 23.0319 8.12586 22.2456L8.1572 22.2352Z' fill='%2361646B'/%3E%3C/svg%3E%0A",
},
{
name: 'Explore',
url: '/explore',
image_url:
"data:image/svg+xml,%3Csvg width='26' height='25' viewBox='0 0 26 25' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M11.58 11.0798L10.3585 14.9846L14.2622 13.7619L15.4848 9.85711L11.58 11.0798ZM9.02384 17.1931C8.79517 17.1931 8.57117 17.1033 8.40434 16.9376C8.17451 16.7066 8.09051 16.3671 8.18851 16.0579L10.047 10.1208C10.1322 9.84545 10.3468 9.63195 10.6198 9.54678L16.557 7.68828C16.8685 7.58911 17.2068 7.67428 17.4378 7.90411C17.6677 8.13511 17.7517 8.47461 17.6537 8.78378L15.7963 14.7209C15.7112 14.9951 15.4953 15.2098 15.2223 15.2949L9.28517 17.1534C9.19884 17.1803 9.11017 17.1931 9.02384 17.1931Z' fill='%2361646B'/%3E%3Cmask id='mask0_1515_2517' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='26' height='25'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0.833374 0.333008H25.0088V24.5083H0.833374V0.333008Z' fill='white'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_1515_2517)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M12.921 2.08301C7.22063 2.08301 2.58313 6.72167 2.58313 12.4208C2.58313 18.1212 7.22063 22.7587 12.921 22.7587C18.6213 22.7587 23.2588 18.1212 23.2588 12.4208C23.2588 6.72167 18.6213 2.08301 12.921 2.08301M12.921 24.5087C6.2558 24.5087 0.83313 19.086 0.83313 12.4208C0.83313 5.75567 6.2558 0.333008 12.921 0.333008C19.5861 0.333008 25.0088 5.75567 25.0088 12.4208C25.0088 19.086 19.5861 24.5087 12.921 24.5087' fill='%2361646B'/%3E%3C/g%3E%3C/svg%3E ",
},
{
name: 'Map',
url: '/map',
image_url:
"data:image/svg+xml,%3Csvg width='18' height='24' viewBox='0 0 18 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0.833496 8.80925C0.833496 4.29425 4.48516 0.642578 9.00016 0.642578C13.5152 0.642578 17.1668 4.29425 17.1668 8.80925C17.1668 13.6742 12.0102 20.3826 9.8985 22.9376C9.43183 23.4976 8.58016 23.4976 8.1135 22.9376C5.99016 20.3826 0.833496 13.6742 0.833496 8.80925ZM6.0835 8.80925C6.0835 10.4192 7.39016 11.7259 9.00016 11.7259C10.6102 11.7259 11.9168 10.4192 11.9168 8.80925C11.9168 7.19924 10.6102 5.89258 9.00016 5.89258C7.39016 5.89258 6.0835 7.19924 6.0835 8.80925Z' fill='%2361646B' /%3E%3C/svg%3E%0A",
},
{
name: 'Trips',
url: '',
image_url:
"data:image/svg+xml,%3Csvg width='26' height='22' viewBox='0 0 26 22' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15.3662 5.53348C14.8832 5.53348 14.4912 5.14148 14.4912 4.65848V1.83398C14.4912 1.35098 14.8832 0.958984 15.3662 0.958984C15.8492 0.958984 16.2412 1.35098 16.2412 1.83398V4.65848C16.2412 5.14148 15.8492 5.53348 15.3662 5.53348Z' fill='%2361646B'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15.3662 20.8318C14.8832 20.8318 14.4912 20.4398 14.4912 19.9568V17.5967C14.4912 17.1125 14.8832 16.7217 15.3662 16.7217C15.8492 16.7217 16.2412 17.1125 16.2412 17.5967V19.9568C16.2412 20.4398 15.8492 20.8318 15.3662 20.8318Z' fill='%2361646B'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M15.3662 14.4634C14.8832 14.4634 14.4912 14.0714 14.4912 13.5884V7.96387C14.4912 7.48087 14.8832 7.08887 15.3662 7.08887C15.8492 7.08887 16.2412 7.48087 16.2412 7.96387V13.5884C16.2412 14.0714 15.8492 14.4634 15.3662 14.4634Z' fill='%2361646B'/%3E%3Cmask id='mask0_1515_513' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='26' height='22'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0.666626 0.666992H25.75V21.0833H0.666626V0.666992Z' fill='white'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_1515_513)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M2.41663 14.1467V16.4088C2.41663 18.0212 3.75013 19.3337 5.38929 19.3337H21.0273C22.6665 19.3337 24 18.0212 24 16.4088V14.1467C22.5405 13.7628 21.4601 12.4422 21.4601 10.8765C21.4601 9.30966 22.5393 7.99016 24 7.60633L23.9988 5.34183C23.9988 3.72949 22.6653 2.41699 21.0261 2.41699H5.39046C3.75129 2.41699 2.41779 3.72949 2.41779 5.34183L2.41663 7.69616C3.89479 8.05899 4.95646 9.32599 4.95646 10.8765C4.95646 12.4422 3.87613 13.7628 2.41663 14.1467ZM21.0273 21.0837H5.38929C2.78529 21.0837 0.666626 18.986 0.666626 16.4088V13.3848C0.666626 12.9018 1.05863 12.5098 1.54163 12.5098C2.45979 12.5098 3.20646 11.7772 3.20646 10.8765C3.20646 10.0015 2.49013 9.34116 1.54163 9.34116C1.30946 9.34116 1.08663 9.24899 0.923293 9.08449C0.758793 8.92116 0.666626 8.69716 0.666626 8.46616L0.667793 5.34183C0.667793 2.76466 2.78646 0.666992 5.39046 0.666992H21.0261C23.6301 0.666992 25.7488 2.76466 25.7488 5.34183L25.75 8.36816C25.75 8.59916 25.6578 8.82316 25.4933 8.98649C25.33 9.15099 25.1071 9.24316 24.875 9.24316C23.9568 9.24316 23.2101 9.97583 23.2101 10.8765C23.2101 11.7772 23.9568 12.5098 24.875 12.5098C25.358 12.5098 25.75 12.9018 25.75 13.3848V16.4088C25.75 18.986 23.6313 21.0837 21.0273 21.0837Z' fill='%2361646B'/%3E%3C/g%3E%3C/svg%3E ",
},
{
name: 'Profile',
url: '/profile',
image_url:
"data:image/svg+xml,%3Csvg width='19' height='24' viewBox='0 0 19 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cmask id='mask0_1515_2027' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='14' width='19' height='10'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M0.166626 14.9121H18.6465V23.5151H0.166626V14.9121Z' fill='white'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_1515_2027)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M9.40767 16.6621C4.4365 16.6621 1.9165 17.5161 1.9165 19.2019C1.9165 20.9029 4.4365 21.7651 9.40767 21.7651C14.3777 21.7651 16.8965 20.9111 16.8965 19.2253C16.8965 17.5243 14.3777 16.6621 9.40767 16.6621ZM9.40767 23.5151C7.12217 23.5151 0.166504 23.5151 0.166504 19.2019C0.166504 15.3566 5.441 14.9121 9.40767 14.9121C11.6932 14.9121 18.6465 14.9121 18.6465 19.2253C18.6465 23.0706 13.3732 23.5151 9.40767 23.5151Z' fill='%2361646B'/%3E%3C/g%3E%3Cmask id='mask1_1515_2027' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='3' y='0' width='13' height='13'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M3.21143 0.333008H15.6015V12.7214H3.21143V0.333008Z' fill='white'/%3E%3C/mask%3E%3Cg mask='url(%23mask1_1515_2027)'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M9.40763 1.99901C6.9098 1.99901 4.87746 4.03017 4.87746 6.52801C4.8693 9.01767 6.88646 11.0477 9.3738 11.057L9.40763 11.89V11.057C11.9043 11.057 13.9355 9.02467 13.9355 6.52801C13.9355 4.03017 11.9043 1.99901 9.40763 1.99901ZM9.40763 12.7218H9.3703C5.9613 12.7113 3.1998 9.93117 3.21146 6.52451C3.21146 3.11201 5.99046 0.333008 9.40763 0.333008C12.8236 0.333008 15.6015 3.11201 15.6015 6.52801C15.6015 9.94401 12.8236 12.7218 9.40763 12.7218Z' fill='%2361646B'/%3E%3C/g%3E%3C/svg%3E ",
},
]
let className = ''
export { className as class }
</script>
<div class="h-full w-full absolute">
<div class={'section w-full h-[calc(100%-100px)] overflow-auto ' + className}><slot /></div>
<div class="footer">
{#each items as { name, url, image_url }}
{#if location.pathname === url}
<FooterItem on:click={() => navigate(url)} active={true} {name} {url}>
<img alt="" style="filter: invert(44%) sepia(66%) saturate(6619%) hue-rotate(222deg) brightness(98%) contrast(88%);" src={image_url} />
</FooterItem>
{:else}
<FooterItem on:click={() => navigate(url)} active={false} {name} {url}>
<img alt="" src={image_url} />
</FooterItem>
{/if}
{/each}
</div>
</div>
<style>
.footer {
width: 100%;
height: 100px;
position: absolute;
background: white;
bottom: 0px;
left: 0px;
border-top: 1px solid #efeff0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
padding: 15px 10px 25px 10px;
}
@media screen and (max-width: 320px) {
.footer {
gap: 0px;
justify-content: space-around;
padding: 10px 0 15px 0;
}
}
</style>

View File

@ -0,0 +1,88 @@
<script>
import GeolocateControl from '@beyonk/svelte-mapbox/src/lib/map/controls/GeolocateControl.svelte'
import Map from './Map.svelte'
import { navigate } from 'svelte-routing'
export let center
//$: console.log(center)
export let mapComponent
export let user = { lat: null, lng: null }
let className = ''
export { className as class }
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition)
} else {
alert("Can't load your location!")
}
function showPosition(position) {
user.lat = position.coords.latitude
user.lng = position.coords.longitude
}
</script>
<Map {center} bind:mapComponent class={className} on:move>
<slot />
<GeolocateControl
on:geolocate={(e) => {
const { latitude, longitude } = e.detail.coords
user = { lat: latitude, lng: longitude }
//console.log(user)
}}
/>
</Map>
<div class="on">
<button on:click={() => navigate('/scanner')}>
<svg viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.77778 8.66765C5.77778 7.07876 7.07778 5.77876 8.66667 5.77876H14.4445C16.0333 5.77876 17.3333 4.47876 17.3333 2.88987C17.3333 1.30098 16.0333 0.000976562 14.4445 0.000976562H5.77778C2.6 0.000976562 0 2.60098 0 5.77876V14.4454C0 16.0343 1.3 17.3343 2.88889 17.3343C4.47778 17.3343 5.77778 16.0343 5.77778 14.4454V8.66765ZM2.88889 34.666C1.3 34.666 0 35.966 0 37.5549V46.2216C0 49.3994 2.6 51.9994 5.77778 51.9994H14.4445C16.0333 51.9994 17.3333 50.6994 17.3333 49.1105C17.3333 47.5216 16.0333 46.2216 14.4445 46.2216H8.66667C7.07778 46.2216 5.77778 44.9216 5.77778 43.3327V37.5549C5.77778 35.966 4.47778 34.666 2.88889 34.666ZM37.5553 0.000976562H46.222C49.3998 0.000976562 51.9998 2.60098 51.9998 5.77876V14.4454C51.9998 16.0343 50.6998 17.3343 49.1109 17.3343C47.522 17.3343 46.222 16.0343 46.222 14.4454V8.66765C46.222 7.07876 44.922 5.77876 43.3331 5.77876H37.5553C35.9664 5.77876 34.6664 4.47876 34.6664 2.88987C34.6664 1.30098 35.9664 0.000976562 37.5553 0.000976562ZM46.2221 43.3327C46.2221 44.9216 44.9221 46.2216 43.3332 46.2216H37.5554C35.9665 46.2216 34.6665 47.5216 34.6665 49.1105C34.6665 50.6994 35.9665 51.9994 37.5554 51.9994H46.2221C49.3998 51.9994 51.9998 49.3994 51.9998 46.2216V37.5549C51.9998 35.966 50.6998 34.666 49.111 34.666C47.5221 34.666 46.2221 35.966 46.2221 37.5549V43.3327ZM28.8881 23.1085H23.1104V28.8838H17.3345V34.6616H23.1123V28.8862H28.8881V23.1085ZM23.1123 28.8862V28.8838H23.1104V28.8862H23.1123ZM28.8912 28.8838H34.6689V34.6616H28.8912V28.8838ZM34.669 17.3302H28.8913V23.108H34.669V17.3302ZM17.3345 17.3301H23.1123V23.1079H17.3345V17.3301Z"
fill="#4263EB"
/>
</svg>
</button>
<button on:click={() => document.getElementsByClassName('mapboxgl-ctrl-geolocate')[0].click()}>
<svg viewBox="0 0 29 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.758179 14.1551C0.758179 6.53234 6.92333 0.367188 14.5461 0.367188C22.1688 0.367188 28.3339 6.53234 28.3339 14.1551C28.3339 22.3687 19.6279 33.6945 16.0627 38.0081C15.2748 38.9536 13.837 38.9536 13.0491 38.0081C9.46424 33.6945 0.758179 22.3687 0.758179 14.1551ZM9.62182 14.1551C9.62182 16.8733 11.8279 19.0793 14.5461 19.0793C17.2642 19.0793 19.4703 16.8733 19.4703 14.1551C19.4703 11.4369 17.2642 9.23083 14.5461 9.23083C11.8279 9.23083 9.62182 11.4369 9.62182 14.1551Z"
fill="#4263EB"
/>
</svg>
</button>
</div>
<style>
.on {
position: absolute;
display: flex;
flex-direction: column;
gap: 27px;
right: 23px;
bottom: 140px;
}
button {
width: 60px;
height: 60px;
border-radius: 25px;
position: relative;
background-color: white;
display: flex;
align-items: center;
justify-content: center;
}
button:hover,
button:active {
opacity: 70%;
}
button > svg {
height: 56%;
width: auto;
}
</style>

View File

@ -0,0 +1,56 @@
<script>
import { Map, controls } from '@beyonk/svelte-mapbox'
import GeolocateControl from '@beyonk/svelte-mapbox/src/lib/map/controls/GeolocateControl.svelte'
import { createEventDispatcher, onMount } from 'svelte'
export let mapComponent
let geolocateControl
let className = ''
export { className as class }
export let radius = false
export let center = { lng: 0, lat: 0 }
/*export const geo = (e) => {
geolocateControl.dispatchEvent('geolocate')
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude } = position.coords
user = { lat: latitude, lng: longitude }
dispatch('move', user)
})
} else {
alert("Can't load your location!")
}
}*/
</script>
<div class={className} class:radius>
<Map
class="h-full"
accessToken="pk.eyJ1IjoiZXJhbnQ0MiIsImEiOiJjbDdybXo4dmswZ3E5M3FwMnFsazdpb3VoIn0.wXblbreOUt3e8N81CAH0Wg"
style="mapbox://styles/mapbox/outdoors-v11"
bind:this={mapComponent}
on:ready={() => {
mapComponent.resize()
mapComponent.setCenter([center.lng, center.lat], 14)
}}
zoom={14}
>
<slot />
</Map>
</div>
<style lang="scss">
div {
overflow: hidden;
&.radius {
border-radius: 25px;
}
}
:global(.mapboxgl-ctrl-top-left) {
display: none;
}
:global(.mapboxgl-marker) {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,48 @@
<script>
import { Marker } from '@beyonk/svelte-mapbox'
import { createEventDispatcher, getContext } from 'svelte'
const dispatch = createEventDispatcher()
export let lat = 0
export let lng = 0
export let user = { lat: 0, lng: 0 }
export let round = (1 / 110.574 / 1000) * 12 //cca 12m nutno pozměnit!!!!!!!!!! tento komentář nemazat
let Mlat = [lat - round, lat + round]
let Mlng = [lng - round, lng + round]
$: isIn = user ? user.lat > Mlat[0] && user.lat < Mlat[1] && user.lng > Mlng[0] && user.lng < Mlng[1] : null
$: isIn && dispatch('enter', { lat, lng, user })
</script>
<Marker popup={false} {lat} {lng}>
<svg width="28" height="27" viewBox="0 0 112 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M64.488 103.367L89.7283 62.4464C106.409 35.4037 86.9542 0.547852 55.1801 0.547852C22.7617 0.547852 3.42399 36.6756 21.4041 63.6502L48.0703 103.656C52.0131 109.571 60.7561 109.417 64.488 103.367Z"
fill="url(#paint0_radial_1068_1071)"
fill-opacity="0.4"
/>
<path
d="M63.4756 91.4251L82.4058 60.4583C94.9161 39.9936 80.3252 13.6162 56.4947 13.6162C32.1808 13.6162 17.6776 40.9562 31.1627 61.3693L51.1623 91.644C54.1194 96.1203 60.6766 96.0037 63.4756 91.4251Z"
fill="url(#paint1_linear_1068_1071)"
/>
<circle cx="55.6164" cy="35.6164" r="11.6164" fill="white" />
<defs>
<radialGradient
id="paint0_radial_1068_1071"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(55.1781 54.2739) rotate(90.2138) scale(47.7265 37.7293)"
>
<stop offset="0.865366" stop-color="#FFD325" />
<stop offset="1" stop-color="#FFE066" stop-opacity="0.1" />
</radialGradient>
<linearGradient id="paint1_linear_1068_1071" x1="40.9078" y1="34.3967" x2="71.9265" y2="73.4203" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFBF00" />
<stop offset="1" stop-color="#DD8706" />
</linearGradient>
</defs>
</svg>
</Marker>

View File

@ -0,0 +1,42 @@
<script>
const col = {
blue: '#4263EB',
white: 'white',
txt_blue: 'white',
txt_white: '#999999',
}
export let background
</script>
<div class="bubble" style={`background: ${col[background]}; color: ${col[`txt_${background}`]}`}>
<slot name="icon" />
<span class="text"><slot /></span>
</div>
<style>
.bubble {
border-radius: 20px;
width: fit-content;
height: 38px;
display: flex;
align-items: center;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 6px 10px;
gap: 10px;
border: 1px solid #4263eb;
font-size: 17px;
color: white;
}
.text {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-size: 20px;
line-height: 25px;
display: flex;
align-items: center;
text-align: center;
}
</style>

View File

@ -0,0 +1,266 @@
<script>
import { onMount } from 'svelte';
// 420 px
export var images = [{"color":"https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg"}, {"color":"https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg"}, {"color":"https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg"}, {"color":"https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg"}, {"color":"https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__480.jpg"}]
let default_index = 3
let x = 0
$: scrollProgress = parseFloat((x/width).toFixed(6))
let width = null
let height = null
let func = 0.1
let mp = [-3, -2, -1]
let nav = []
for (let i = 0; i < images.length + 3; i++)
{
mp.push(mp.at(-1) + 1)
}
for (let i = 0; i < images.length ; i++)
{
if (i === 0){
nav.push({"status":"active"})
}
else{
nav.push({"status":"wait"})
}
}
let carousel
onMount(() => {
onLoad(carousel)
window.onresize = () =>{onLoad(carousel)}
});
function onLoad(self) {
width = self.getBoundingClientRect().width/100*70
height = self.getBoundingClientRect().width/100*70 / 1.5
images=images;
if (self.getBoundingClientRect().width < 440){
func = 0.05
}
else{
func = 0.1
}
}
function getWidth(x){
var res = width - width*0.3*Math.abs(x)
if (res > 0){return res}
else{return 0}
}
function getMargin(x){
var m = 1000
if (x<=-1.75){
x = -(1.75+(x+1.75))
if (x>=0){
x=0
}
}
else if (x>=1.75){
x = 1.75-(x-1.75)
if (x<=0){
x=0
}
}
if (x > 0){
x = Math.abs(x)
return "margin-left: -"+ m*func*x +"px"
}
else if (x === 0){return "margin: 0px;"}
else{
x = Math.abs(x)
return "margin-right: -" + m*func*x +"px"
}
}
function zIndex(x){
if (x>-0.5&&x<0.5){
return 2
}
else if ((x>-1.5&&x<=-0.5)||(x>=0.5&&x<1.5))
{
return 1
}
else if ((x<=-2.5)||(x>=2.5))
{
return -1
}
else
{
return 0
}
}
</script>
<div class="container">
<div id=carousel bind:this={carousel} on:scroll={() => x=carousel.scrollLeft} style="height: {height}px;">
<div class="sticky">
{#each Array(default_index) as _, index (index)}
<div class="item fake" style="
display: {mp.at(index)-scrollProgress<=-3.5||mp.at(index)-scrollProgress>3.5 ? "none" : "block"};
min-width: {getWidth(mp.at(index)-scrollProgress)}px;
max-width: {getWidth(mp.at(index)-scrollProgress)}px;
min-height: {height + height*(-func*(mp.at(index)-scrollProgress)*(mp.at(index)-scrollProgress))}px;
z-index: {zIndex(mp.at(index)-scrollProgress)};
{getMargin(mp.at(index)-scrollProgress)};">
</div>
{/each}
{#each images as image, index (index)}
<div class="item real" style="text-align:center;
display: {mp.at(index+default_index)-scrollProgress<=-3.5||mp.at(index+default_index)-scrollProgress>3.5 ? "none" : "block"};
min-width: {getWidth(mp.at(index+default_index)-scrollProgress)}px;
max-width: {getWidth(mp.at(index+default_index)-scrollProgress)}px;
z-index: {zIndex(mp.at(index+default_index)-scrollProgress)};
{getMargin(mp.at(index+default_index)-scrollProgress)}">
<img src={image.color}>
</div>
{/each}
{#each Array(3) as _, index (index)}
<div class="item fake" style="
display: {mp.at(-default_index+index)-scrollProgress<=-3.5||mp.at(-default_index+index)-scrollProgress>3.5 ? "none" : "block"};
min-width: {getWidth(mp.at(-default_index+index)-scrollProgress)}px;
max-width: {getWidth(mp.at(-default_index+index)-scrollProgress)}px;
min-height: {height + height*(-func*(mp.at(-default_index+index)-scrollProgress)*(mp.at(-default_index+index)-scrollProgress))}px;
z-index: {zIndex(mp.at(-default_index+index)-scrollProgress)};
{getMargin(mp.at(-default_index+index)-scrollProgress)}">
</div>
{/each}
<div class="overlay">
</div>
</div>
{#each Array(images.length-1) as _}
<div class="scroll-guide" style="min-width: {(width)}px; min-height: 10px; scroll-snap-align: start end;">
</div>
{/each}
</div>
<div class="slide-navigation">
{#each Array(images.length) as _, index (index)}
{#if Math.round(scrollProgress) == index}
<span class="active"></span>
{:else}
<span></span>
{/if}
{/each}
</div>
</div>
<style>
.container{
display: flex;
flex-direction: column;
align-items: center;
}
#carousel{
margin-top: 20px;
margin-bottom: 10px;
display: flex;
flex-direction: row;
align-items: center;
max-width: 100%;
min-width: 100%;
width: 100%;
position: relative;
scroll-snap-type: x mandatory;
overflow-x: scroll;
scroll-behavior: auto;
}
/* Hide scrollbar for Chrome, Safari and Opera */
#carousel::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
#carousel {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
#carousel > .sticky{
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: sticky;
left: 0;
top: 0;
gap: 0px;
min-width: 100%;
max-width: 100%;
width: 100%;
overflow: hidden;
scroll-snap-align: end;
}
#carousel > .sticky > .item{
border-radius: 10px;
min-width: 0;
color: white;
overflow: hidden;
}
#carousel > .sticky > .overlay{
position: absolute;
left: 0px;
top: 0;
width: 100%;
height: 100%;
/* Mask */
background: linear-gradient(270deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.1) 20%, rgba(255, 255, 255, 0) 40%, rgba(255, 255, 255, 0) 50.56%, rgba(255, 255, 255, 0) 60%, rgba(255, 255, 255, 0.1) 80%, rgba(255, 255, 255, 0.6) 100%);
transform: matrix(1, 0, 0, -1, 0, 0);
z-index: 10;
}
#carousel > .sticky > .real{
display: flex;
align-items: stretch;
justify-content: stretch;
position: relative;
}
#carousel > .sticky > .real > img{
flex-grow: 1;
}
#carousel > .sticky > .fake {
background-color: white;
}
.slide-navigation{
height: fit-content;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-evenly;
gap: 5px;
width: 80px;
}
.slide-navigation > span{
width: 6px;
height: 6px;
border-radius: 6px;
background-color: #999999;
}
.slide-navigation > .active{
background-color: #7B61FF;
}
</style>

View File

@ -0,0 +1,14 @@
<div>
<slot />
</div>
<style>
div {
width: 100vw;
position: relative;
left: 50%;
right: 50%;
margin-left: -50vw;
margin-right: -50vw;
}
</style>

View File

@ -0,0 +1,17 @@
<script>
export let src
let className
export { className as class }
</script>
<section class={className} style="--bg: url({src});" />
<style lang="scss">
section {
border-radius: 26px;
background-image: var(--bg);
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
}
</style>

View File

@ -0,0 +1,35 @@
<script>
import Image from './Image.svelte'
export let images
let windowsize
let overflow = 'hidden'
const nextImages = () => {
overflow = 'auto'
images = images
windowsize = Infinity
}
</script>
<div style="overflow-y: {overflow}" bind:offsetWidth={windowsize} class="imageSlider">
{#each images as image, i}
<!--{#if Math.floor(windowsize / (94 + 10)) >= i + 1}-->
<div>
<Image src={image} class="w-24 h-24" />
<!--{#if Math.floor(windowsize / (94 + 10)) === i + 1 && images.length !== i + 1}
<span on:click={nextImages} class="imageCounter">{`+${images.length - Math.floor(windowsize / (94 + 10))}`}</span>
{/if}-->
</div>
<!--{/if}-->
{/each}
</div>
<style>
.imageSlider {
display: flex;
justify-content: space-between;
width: 100%;
gap: 10px;
position: relative;
}
</style>

View File

@ -0,0 +1,12 @@
<script>
let className = ''
export { className as class }
</script>
<div class={'w-full h-min ' + className} />
<style>
div {
border-top: 1px solid #efeff0;
}
</style>

View File

@ -0,0 +1,44 @@
<div class="lds-ring">
<div />
<div />
<div />
<div />
</div>
<style>
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border: 8px solid #4263eb;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #4263eb transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@ -0,0 +1,42 @@
<script lang="ts">
export let active: boolean = false
export let name: string = ''
export let url: string = ''
</script>
<button on:click>
<slot />
{#if active}
<p class="active" style="color:#4263EB">{name}</p>
{:else}
<p>{name}</p>
{/if}
</button>
<style>
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@500&display=swap');
button {
height: 54px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
}
button:hover,
button:active {
opacity: 70%;
}
button > p {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
font-size: 11px;
line-height: 16px;
text-align: center;
color: #61646b;
}
</style>

View File

@ -0,0 +1,9 @@
<script>
import { onMount } from 'svelte'
import { navigate } from 'svelte-routing'
export let to = '/'
export let replace = false
onMount(() => navigate(to, { replace }))
</script>

View File

@ -0,0 +1,36 @@
<script>
export let style = ''
export let title = ''
let className = ''
export { className as class }
</script>
<section class={className} {style}>
<h2><slot name="title">{title}</slot></h2>
<div><slot /></div>
</section>
<style lang="scss">
section {
display: flex;
flex-direction: column;
row-gap: 20px;
width: 100%;
h2 {
font-style: normal;
font-weight: 600;
font-size: 24px;
line-height: 30px;
color: #110042;
}
div {
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 25px;
}
}
</style>

View File

@ -0,0 +1,8 @@
<script>
let className = ''
export { className as class }
</script>
<div class={'w-full h-auto flex flex-wrap flex-row justify-normal items-center gap-4 ' + className}>
<slot />
</div>

View File

@ -1,22 +0,0 @@
import { Client, Account, Databases, Storage, Teams, Functions, Locale, Avatars } from 'appwrite'
const client = new Client()
const account = new Account(client)
const databases = new Databases(client)
const storage = new Storage(client)
const teams = new Teams(client)
const functions = new Functions(client)
const locale = new Locale(client)
const avatars = new Avatars(client)
const url = {
oauth: {
success: `${import.meta.env.VITE_HOSTNAME}/oauth/success`,
failure: `${import.meta.env.VITE_HOSTNAME}/oauth/failure`
}
}
client.setEndpoint(import.meta.env.VITE_APPWRITE_ENDPOINT).setProject(import.meta.env.VITE_APPWRITE_PROJECT_ID)
export default client
export { client, account, url, databases, storage, teams, functions, locale, avatars }

View File

@ -1,31 +0,0 @@
import type { Models, RealtimeResponseEvent } from 'appwrite'
import { writable } from 'svelte/store'
import { account, client } from './appwrite'
const userStore = writable<Models.Account<Models.Preferences>>(null)
const loadingStore = writable(true)
client.subscribe('account', (response: RealtimeResponseEvent<any>) => {
if (response.events.includes('users.*.sessions.*.delete')) {
return userStore.set(null)
}
if (response.events.includes('users.*.sessions.*.update')) {
return userStore.set(response.payload)
}
})
account.get().then(data => {
userStore.set(data)
loadingStore.set(false)
})
const user = {
subscribe: userStore.subscribe,
logout: () => account.deleteSession('current'),
account: account
}
const isLoading = { subscribe: loadingStore.subscribe }
export { account, user, isLoading }

View File

@ -1,15 +0,0 @@
<div>
<slot />
</div>
<style lang="scss">
div {
background-color: rgba(0, 0, 0, 0.1);
border-radius: 0.25rem;
color: rgba(0, 0, 0, 0.7);
box-shadow: #d1d5db 0px -4px 0px inset, rgba(0, 0, 0, 0.4) 0px 1px 1px;
padding: 0.25rem 0.5rem;
}
</style>

View File

@ -1,34 +0,0 @@
<script lang="ts">
import { link } from '$lib/router'
export let href: string | null = null
let className = ''
export { className as class }
const isValidHttpUrl = (string) => {
let url: URL
try {
url = new URL(string)
} catch (_) {
return false
}
return url.protocol === 'http:' || url.protocol === 'https:'
}
</script>
{#if href}
{#if isValidHttpUrl(href)}
<a {href} class={className}>
<slot />
</a>
{:else}
<a {href} class={className} use:link>
<slot />
</a>
{/if}
{:else}
<button class={className} on:click>
<slot />
</button>
{/if}

View File

@ -1,5 +0,0 @@
<div class="h-full w-full top-0 left-0 flex justify-center items-center">
<div class="animate-spin inline-block w-6 h-6 border-[3px] border-current border-t-transparent text-current rounded-full" role="status" aria-label="loading">
<span class="sr-only">Loading...</span>
</div>
</div>

View File

@ -1,14 +0,0 @@
<script lang="ts">
export let columns = 3
export let gap = 4
</script>
<div class="masonry-grid columns-{columns} gap-x-{gap}">
<slot />
</div>
<style lang="scss">
:global(div.masonry-grid *) {
break-inside: avoid;
}
</style>

View File

@ -1,9 +0,0 @@
<div class="flex">
<aside class="sidebar__sidebar w-[30%]">
<slot name="aside" />
</aside>
<main class="overflow-auto flex-1">
<slot />
</main>
</div>

View File

@ -1,23 +0,0 @@
<script lang="ts">
export let space = '3rem'
</script>
<div class="layout-stack" style="--space: {space};">
<slot />
</div>
<style lang="scss">
:global(.layout-stack) {
display: flex;
flex-direction: column;
justify-content: flex-start;
& > * {
margin-block: 0;
}
& > * + * {
margin-block-start: var(--space, 1.5rem);
}
}
</style>

View File

@ -1,178 +0,0 @@
import { writable } from 'svelte/store'
import { databases, client } from './appwrite'
import { Models, Query, RealtimeResponseEvent } from 'appwrite'
import { ID } from 'appwrite'
import type { Writable } from 'svelte/store'
class Collection {
constructor(protected databaseId: string, protected collectionId: string) { }
createDocument(data: { [key: string]: any } = {}, permissions: string[] = null) {
return databases.createDocument(this.databaseId, this.collectionId, ID.unique(), data, permissions)
}
updateDocument(documentId: string, data: { [key: string]: any } = {}, permissions: string[] = null) {
if (permissions.length === 0 && Object.keys(data).length === 0) return
return databases.updateDocument(this.databaseId, this.collectionId, documentId, data, permissions)
}
deleteDocument(documentId: string) {
return databases.deleteDocument(this.databaseId, this.collectionId, documentId)
}
createObserver() {
const dataStore = writable<Models.Document[]>([])
client.subscribe(`databases.${this.databaseId}.collections.${this.collectionId}.documents`, (response: RealtimeResponseEvent<any>) => {
if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.*.create`)) {
dataStore.update(current => {
current.push(response.payload)
return current
})
this.subscribeCollectionUpdate(response.payload, dataStore)
}
})
return { subscribe: dataStore.subscribe }
}
createSubscriber(queries: string[] = []) {
const loadingStore = writable(true)
const dataStore = writable<Models.Document[]>([])
databases.listDocuments(this.databaseId, this.collectionId, queries).then(data => {
data.documents.forEach((document) => this.subscribeCollectionUpdate(document, dataStore))
dataStore.set(data.documents)
loadingStore.set(false)
})
return [{ subscribe: dataStore.subscribe }, { subscribe: loadingStore.subscribe }] as const
}
createPaginate(limit: number, queries: string[] = []) {
const dataStore = writable<Models.Document[]>([])
const loadingStore = writable(true)
let offset = 0
const store = {
subscribe: dataStore.subscribe,
async next() {
const data = await databases.listDocuments(this.databaseId, this.collectionId, [...queries, Query.limit(limit), Query.offset(offset)])
data.documents.forEach((document) => this.subscribeCollectionUpdate(document, dataStore))
dataStore.update(current => [...current, ...data.documents])
offset += limit
}
}
store.next().then(() => loadingStore.set(false))
return [store, { subscribe: loadingStore.subscribe }] as const
}
createInfinityScrollDispatcher(limit: number, queries: string[] = [], observerOptions: IntersectionObserverInit = {}) {
const dataStore = writable<Models.Document[]>([])
let lastId: string = null
databases.listDocuments(this.databaseId, this.collectionId, [...queries, Query.limit(limit)]).then(firstData => {
dataStore.set(firstData.documents)
firstData.documents.forEach((document) => this.subscribeCollectionUpdate(document, dataStore))
lastId = firstData.documents[firstData.documents.length - 1].$id
})
const observer = new IntersectionObserver((entries, me) => {
if (lastId === null) return
entries.forEach(entry => {
if (!entry.isIntersecting) return
databases.listDocuments(this.databaseId, this.collectionId, [...queries, Query.limit(limit), Query.cursorAfter(lastId)]).then((data) => {
dataStore.update(current => {
current.push(...data.documents)
lastId = current[current.length - 1].$id
return current
})
data.documents.forEach((document) => this.subscribeCollectionUpdate(document, dataStore))
entry.target.dispatchEvent(new CustomEvent('fetch', entry.target as CustomEventInit<HTMLElement>))
})
})
}, observerOptions)
const directive = (node: HTMLElement) => {
observer.observe(node)
return {
destroy() {
observer.disconnect()
}
}
}
return [{ subscribe: dataStore.subscribe }, directive] as const
}
protected subscribeCollectionUpdate(document: Models.Document, store: Writable<Models.Document[]>) {
client.subscribe(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${document.$id}`, (response: RealtimeResponseEvent<any>) => {
if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${document.$id}.delete`)) {
store.update(current => {
current.splice(current.indexOf(document), 1)
return current
})
return
}
if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${document.$id}.update`)) {
store.update(current => {
current[current.indexOf(document)] = response.payload
return current
})
return
}
})
}
}
class Document {
constructor(protected databaseId: string, protected collectionId: string, protected documentId: string) { }
createSubscriber() {
const dataStore = writable<Models.Document>(null)
const loadingStore = writable(true)
databases.getDocument(this.databaseId, this.collectionId, this.documentId).then(data => {
dataStore.set(data)
loadingStore.set(false)
})
client.subscribe(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${this.documentId}`, (response: RealtimeResponseEvent<any>) => {
if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${this.documentId}.update`)) {
dataStore.set(response.payload)
return
}
if (response.events.includes(`databases.${this.databaseId}.collections.${this.collectionId}.documents.${this.documentId}.delete`)) {
dataStore.set(null)
return
}
})
return [{ subscribe: dataStore.subscribe }, { subscribe: loadingStore.subscribe }] as const
}
delete() {
return databases.deleteDocument(this.databaseId, this.collectionId, this.documentId)
}
update(data: { [key: string]: any } = {}, permissions: string[] = []) {
if (permissions.length === 0 && Object.keys(data).length === 0) return
return databases.updateDocument(this.databaseId, this.collectionId, this.documentId, data, permissions)
}
}
export { Collection, Document }

View File

@ -1,8 +0,0 @@
import { Route, Router, link, links, Link } from 'svelte-routing'
import { navigate, back, forward } from './router/navigate'
import Redirect from "./router/Redirect.svelte"
import ProtectedRoute from "./router/ProtectedRoute.svelte"
import LazyRoute from "./router/LazyRoute.svelte"
import defineRoutes from "./router/routes"
export { Route, Router, Link, link, links, navigate, back, forward, Redirect, ProtectedRoute, LazyRoute, defineRoutes }

View File

@ -1,14 +0,0 @@
<script lang="ts">
import type { ComponentType, SvelteComponentTyped } from 'svelte'
export let path: string
export let component: () => Promise<any>
export let loading: ComponentType<SvelteComponentTyped<any>> | null = null
import { Route } from '$lib/router'
import LazyRouteGuard from './LazyRouteGuard.svelte'
</script>
<Route {path} let:location let:params>
<LazyRouteGuard {location} {params} {component} {loading} />
</Route>

View File

@ -1,33 +0,0 @@
<script lang="ts">
import { onMount, SvelteComponent } from 'svelte'
import type { ComponentType, SvelteComponentTyped } from 'svelte'
import type { RouteLocation, RouteParams } from 'svelte-routing/types/Route'
export let component: (() => Promise<any>) | ComponentType<SvelteComponentTyped<any>>
export let loading: ComponentType<SvelteComponentTyped<any>> | null = null
export let location: RouteLocation
export let params: RouteParams
export let before: (() => any) | null = null
let loadedComponent = null
const __before = async () => (before ? await before() : null)
onMount(() => {
__before()
if (component instanceof SvelteComponent) {
loadedComponent = component
return
}
;(component as () => Promise<any>)().then((module) => {
loadedComponent = module.default
})
})
</script>
{#if loadedComponent}
<svelte:component this={loadedComponent} {params} {location} />
{:else if loading}
<svelte:component this={loading} {params} {location} />
{/if}

View File

@ -1,19 +0,0 @@
<script lang="ts">
import { Route } from 'svelte-routing'
import ProtectedRouteGuard from './ProtectedRouteGuard.svelte'
export let path: string
export let fallback = '/'
export let allow = true
export let component: any = null
</script>
<Route {path} let:params let:location>
<ProtectedRouteGuard {allow} {fallback} {location}>
{#if component !== null}
<svelte:component this={component} {location} {params} />
{:else}
<slot {params} {location} />
{/if}
</ProtectedRouteGuard>
</Route>

View File

@ -1,14 +0,0 @@
<script lang="ts">
import type { RouteLocation } from 'svelte-routing/types/Route'
import Redirect from './Redirect.svelte'
export let fallback: string
export let allow: boolean
export let location: RouteLocation
</script>
{#if allow}
<slot />
{:else}
<Redirect to={fallback} replace state={{ from: location.pathname }} />
{/if}

View File

@ -1,15 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte'
import { navigate } from 'svelte-routing'
export let to: string
export let state: any = null
export let replace = false
export let then: (to: string) => any = () => null
onMount(() => {
navigate(to, { replace, state })
then(to)
})
</script>

View File

@ -1,35 +0,0 @@
<script lang="ts">
import { ComponentType, SvelteComponentTyped } from 'svelte'
import { Route, Router } from 'svelte-routing'
import LazyRouteGuard from './LazyRouteGuard.svelte'
interface Route {
path: string
component: ComponentType<SvelteComponentTyped<any>> | (() => Promise<any>)
before?: () => any
layout?: ComponentType<SvelteComponentTyped<any>>
loading?: ComponentType<SvelteComponentTyped<any>>
}
export let layout: ComponentType<SvelteComponentTyped<any>> | null = null
export let loading: ComponentType<SvelteComponentTyped<any>> | null = null
export let error: ComponentType<SvelteComponentTyped<any>> | null = null
export let routes: Route[] = []
</script>
<Router>
{#each routes as route}
<Route path={route.path} let:location let:params>
{#if route?.layout || layout}
<svelte:component this={route?.layout ?? layout}>
<LazyRouteGuard {location} {params} before={route?.before} loading={route?.loading ?? loading} component={route.component} />
</svelte:component>
{:else}
<LazyRouteGuard {location} {params} before={route?.before} loading={route?.loading ?? loading} component={route.component} />
{/if}
</Route>
{#if error}
<Route path="/*" component={error} />
{/if}
{/each}
</Router>

View File

@ -1,9 +0,0 @@
import { navigate as nav } from 'svelte-routing'
export const navigate = (to: string | number, options?: { replace?: boolean, state?: { [k in string | number]: unknown } }) => {
if (typeof to === 'string') return nav(to, options)
window.history.go(to)
}
export const back = () => window.history.back()
export const forward = () => window.history.forward()

View File

@ -1,29 +0,0 @@
import { ComponentType, SvelteComponentTyped } from 'svelte'
export interface Route {
component: () => Promise<any>,
path: string,
layout?: ComponentType<SvelteComponentTyped<any>>,
loading?: ComponentType<SvelteComponentTyped<any>>,
before?: () => any,
}
interface RouteDefinition {
component: () => Promise<any>,
path: string,
layout: ComponentType<SvelteComponentTyped<any>> | null,
loading: ComponentType<SvelteComponentTyped<any>> | null,
before: () => any | null,
}
interface RouteConfig {
routes: Route[],
layout?: ComponentType<SvelteComponentTyped<any>> | null,
loading?: ComponentType<SvelteComponentTyped<any>> | null,
}
const defineRoutes = (config: RouteConfig): RouteDefinition[] => {
return config.routes.map(route => ({ ...route, layout: route?.layout ?? config?.layout ?? null, loading: route?.loading ?? config?.loading ?? null, before: route?.before ?? null }))
}
export default defineRoutes

View File

@ -1,186 +0,0 @@
import { storage, client } from './appwrite'
import { ID, Models, RealtimeResponseEvent } from 'appwrite'
import { Writable, writable } from 'svelte/store'
class Bucket {
constructor(protected bucketId: string) { }
createFile(file, permissions: string[] = []) {
return storage.createFile(this.bucketId, ID.unique(), file, permissions)
}
deleteFile(file: string | Models.File) {
return storage.deleteFile(this.bucketId, typeof file === 'string' ? file : file.$id)
}
updateFile(file: string | Models.File, permissions: string[] = []) {
return storage.updateFile(this.bucketId, typeof file === 'string' ? file : file.$id, permissions)
}
getFilePreview(file: string | Models.File) {
return storage.getFilePreview(this.bucketId, typeof file === 'string' ? file : file.$id)
}
getFileDownload(file: string | Models.File) {
return storage.getFileDownload(this.bucketId, typeof file === 'string' ? file : file.$id)
}
getFileView(file: string | Models.File) {
return storage.getFileView(this.bucketId, typeof file === 'string' ? file : file.$id)
}
getFileContent(file: string | Models.File) {
const fileContent = writable('')
const loading = writable(true)
const { href } = storage.getFileView(this.bucketId, typeof file === 'string' ? file : file.$id)
this.subscribeFileUpdateCallback(file, () => fetch(href).then(res => res.ok ? res.text() : null).then(res => {
fileContent.set(res ?? '')
loading.set(false)
}))
fetch(href).then(res => res.ok ? res.text() : null).then(res => {
fileContent.set(res ?? '')
loading.set(false)
})
return [{ subscribe: fileContent.subscribe }, { subscribe: loading.subscribe }] as const
}
createUploadDispatcher(acceptManyFiles = false) {
let files = []
const eventUploadDirective = (node: HTMLInputElement) => {
const eventListener = (e) => files = acceptManyFiles ? Array.from(e.target.files) : [e.target.files[0]]
node.addEventListener('change', eventListener)
acceptManyFiles && node.setAttribute('multiple', 'multiple')
return {
destroy() {
node.removeEventListener('change', eventListener)
}
}
}
const dispatchUpload = (permissions: string[] = []) => {
return Promise.all(files.map(file => this.createFile(file, permissions)))
}
return [eventUploadDirective, dispatchUpload] as const
}
createSubsciber(queries: string[] = [], search = '') {
const filesStore = writable<Models.File[]>([])
const loadingStore = writable(true)
storage.listFiles(this.bucketId, queries, search).then(({ files }) => {
files.forEach(file => this.subscribeFileUpdate(file, filesStore))
filesStore.set(files)
loadingStore.set(false)
})
return [{ subscribe: filesStore.subscribe }, { subscribe: loadingStore.subscribe }] as const
}
createObserver() {
const dataStore = writable<Models.File[]>([])
client.subscribe(`buckets.${this.bucketId}.files`, (response: RealtimeResponseEvent<any>) => {
if (response.events.includes(`buckets.${this.bucketId}.files.*.create`)) {
dataStore.update(current => {
current.push(response.payload)
return current
})
this.subscribeFileUpdate(response.payload, dataStore)
}
})
return { subscribe: dataStore.subscribe }
}
protected subscribeFileUpdate(file: Models.File, filesStore: Writable<Models.File[]>) {
this.subscribeFileUpdateCallback(file, ({ event }) => {
if (event === 'update') return filesStore.update(current => {
current[current.indexOf(file)] = file
return current
})
filesStore.update(current => {
current.splice(current.indexOf(file), 1)
return current
})
})
}
protected subscribeFileUpdateCallback(file: string | Models.File, callback: ({ fileId, event }: { fileId: string, event: 'update' | 'delete' }) => any) {
client.subscribe(`buckets.${this.bucketId}.files.${typeof file === 'string' ? file : file.$id}`, (response: RealtimeResponseEvent<any>) => {
if (response.events.includes(`buckets.${this.bucketId}.files.${typeof file === 'string' ? file : file.$id}.update`)) {
return callback({ fileId: typeof file === 'string' ? file : file.$id, event: 'update' })
}
if (response.events.includes(`buckets.${this.bucketId}.files.${typeof file === 'string' ? file : file.$id}.delete`)) {
return callback({ fileId: typeof file === 'string' ? file : file.$id, event: 'delete' })
}
})
}
}
class File {
constructor(protected bucketId: string, protected fileId: string) { }
createSubscriber() {
const fileStore = writable<Models.File>(null)
const loadingStore = writable(true)
storage.getFile(this.bucketId, this.fileId).then((result) => {
fileStore.set(result)
loadingStore.set(false)
})
client.subscribe(`buckets.${this.bucketId}.files.${this.fileId}`, (response: RealtimeResponseEvent<any>) => {
if (response.events.includes(`buckets.${this.bucketId}.files.${this.fileId}.update`)) {
fileStore.set(response.payload)
return
}
if (response.events.includes(`buckets.${this.bucketId}.files.${this.fileId}.delete`)) {
fileStore.set(null)
return
}
})
return [{ subscribe: fileStore.subscribe }, { subscribe: loadingStore.subscribe }] as const
}
delete() {
return storage.deleteFile(this.bucketId, this.fileId)
}
update(permissions: string[] = []) {
return storage.updateFile(this.bucketId, this.fileId, permissions)
}
getPreview() {
return storage.getFilePreview(this.bucketId, this.fileId)
}
getDownload() {
return storage.getFileDownload(this.bucketId, this.fileId)
}
getView() {
return storage.getFileView(this.bucketId, this.fileId)
}
async getContent() {
const { href } = await storage.getFileView(this.bucketId, this.fileId)
return await fetch(href)
}
}
export { Bucket, File }

3
src/lib/stores/game.js Normal file
View File

@ -0,0 +1,3 @@
import { writable } from 'svelte/store'
export const data = writable(null)

8
src/lib/svg/Earth.svelte Normal file
View File

@ -0,0 +1,8 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M15 2.5C8.1 2.5 2.5 8.1 2.5 15C2.5 21.9 8.1 27.5 15 27.5C21.9 27.5 27.5 21.9 27.5 15C27.5 8.1 21.9 2.5 15 2.5ZM13.75 24.9125C8.8125 24.3 5 20.1 5 15C5 14.225 5.1 13.4875 5.2625 12.7625L11.25 18.75V20C11.25 21.375 12.375 22.5 13.75 22.5V24.9125ZM20 20C21.125 20 22.05 20.725 22.375 21.7375C24 19.9625 25 17.6 25 15C25 10.8125 22.4125 7.225 18.75 5.7375V6.25C18.75 7.625 17.625 8.75 16.25 8.75H13.75V11.25C13.75 11.9375 13.1875 12.5 12.5 12.5H10V15H17.5C18.1875 15 18.75 15.5625 18.75 16.25V20H20Z"
fill="#4263EB"
/>
</svg>

After

Width:  |  Height:  |  Size: 677 B

14
src/lib/svg/Eye.svelte Normal file
View File

@ -0,0 +1,14 @@
<script lang="ts">
export let active:boolean = false
</script>
{#if active}
<svg width="28" height="20" viewBox="0 0 28 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 0.625C7.75 0.625 2.4125 4.5125 0.25 10C2.4125 15.4875 7.75 19.375 14 19.375C20.25 19.375 25.5875 15.4875 27.75 10C25.5875 4.5125 20.25 0.625 14 0.625ZM14 16.25C10.55 16.25 7.75 13.45 7.75 10C7.75 6.55 10.55 3.75 14 3.75C17.45 3.75 20.25 6.55 20.25 10C20.25 13.45 17.45 16.25 14 16.25ZM10.25 10C10.25 7.925 11.925 6.25 14 6.25C16.075 6.25 17.75 7.925 17.75 10C17.75 12.075 16.075 13.75 14 13.75C11.925 13.75 10.25 12.075 10.25 10Z" fill="black" fill-opacity="1"/>
</svg>
{:else}
<svg width="28" height="20" viewBox="0 0 28 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 0.625C7.75 0.625 2.4125 4.5125 0.25 10C2.4125 15.4875 7.75 19.375 14 19.375C20.25 19.375 25.5875 15.4875 27.75 10C25.5875 4.5125 20.25 0.625 14 0.625ZM14 16.25C10.55 16.25 7.75 13.45 7.75 10C7.75 6.55 10.55 3.75 14 3.75C17.45 3.75 20.25 6.55 20.25 10C20.25 13.45 17.45 16.25 14 16.25ZM10.25 10C10.25 7.925 11.925 6.25 14 6.25C16.075 6.25 17.75 7.925 17.75 10C17.75 12.075 16.075 13.75 14 13.75C11.925 13.75 10.25 12.075 10.25 10Z" fill="black" fill-opacity="0.3"/>
</svg>
{/if}

View File

@ -0,0 +1,16 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.04 12.2615C23.04 11.446 22.9668 10.6619 22.8309 9.90918H12V14.3576H18.1891C17.9225 15.7951 17.1123 17.013 15.8943 17.8285V20.714H19.6109C21.7855 18.7119 23.04 15.7637 23.04 12.2615Z" fill="#4285F4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9999 23.4996C15.1049 23.4996 17.7081 22.4698 19.6108 20.7134L15.8943 17.828C14.8645 18.518 13.5472 18.9257 11.9999 18.9257C9.00471 18.9257 6.46948 16.9028 5.56516 14.1846H1.72311V17.1641C3.61539 20.9225 7.50448 23.4996 11.9999 23.4996Z" fill="#34A853"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.56523 14.1855C5.33523 13.4955 5.20455 12.7584 5.20455 12.0005C5.20455 11.2425 5.33523 10.5055 5.56523 9.81548V6.83594H1.72318C0.944318 8.38844 0.5 10.1448 0.5 12.0005C0.5 13.8562 0.944318 15.6125 1.72318 17.165L5.56523 14.1855Z" fill="#FBBC05"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9999 5.07386C13.6883 5.07386 15.2043 5.65409 16.3961 6.79364L19.6945 3.49523C17.7029 1.63955 15.0997 0.5 11.9999 0.5C7.50448 0.5 3.61539 3.07705 1.72311 6.83545L5.56516 9.815C6.46948 7.09682 9.00471 5.07386 11.9999 5.07386Z" fill="#EA4335"/>
</svg>
<style>
@media screen and (max-width: 330px) {
svg{
width: 7.2vw;
height: auto;
}
}
</style>

After

Width:  |  Height:  |  Size: 1.4 KiB

8
src/lib/svg/Help.svelte Normal file
View File

@ -0,0 +1,8 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M15 2.5C8.1 2.5 2.5 8.1 2.5 15C2.5 21.9 8.1 27.5 15 27.5C21.9 27.5 27.5 21.9 27.5 15C27.5 8.1 21.9 2.5 15 2.5ZM13.75 23.75V21.25H16.25V23.75H13.75ZM17.7125 15.2125L18.8375 14.0625C20.1125 12.7875 20.55 10.6 19.075 8.5625C17.95 7 16.1375 6.0125 14.2375 6.3125C12.3 6.6125 10.7375 8.0125 10.2 9.85C10 10.55 10.5 11.25 11.225 11.25H11.6C12.0875 11.25 12.475 10.9 12.625 10.4375C13.0375 9.25 14.325 8.4625 15.7125 8.85C16.5875 9.1 17.2875 9.8625 17.45 10.7625C17.6125 11.6375 17.3375 12.4625 16.7625 13.0125L15.2125 14.5875C14.6875 15.1125 14.275 15.7625 14.025 16.4875C13.85 17 13.75 17.55 13.75 18.125V18.75H16.25C16.25 18.175 16.3125 17.725 16.4125 17.325C16.6375 16.425 17.0875 15.85 17.7125 15.2125Z"
fill="#4263EB"
/>
</svg>

After

Width:  |  Height:  |  Size: 882 B

View File

@ -0,0 +1,27 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="98" height="98" rx="49" fill="#EFEFF0" />
<mask id="mask0_1661_3075" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="40" y="40" width="20" height="20">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.0005 40H59.9601V59.9498H40.0005V40Z" fill="white" />
</mask>
<g mask="url(#mask0_1661_3075)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M45.6505 41.5C43.1295 41.5 41.5005 43.227 41.5005 45.899V54.051C41.5005 56.724 43.1295 58.45 45.6505 58.45H54.3005C56.8275 58.45 58.4605 56.724 58.4605 54.051V45.899C58.4605 43.227 56.8275 41.5 54.3005 41.5H45.6505ZM54.3005 59.95H45.6505C42.2705 59.95 40.0005 57.579 40.0005 54.051V45.899C40.0005 42.371 42.2705 40 45.6505 40H54.3005C57.6855 40 59.9605 42.371 59.9605 45.899V54.051C59.9605 57.579 57.6855 59.95 54.3005 59.95Z"
fill="#AFB1B6"
/>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M43.2815 55.1799C43.0955 55.1799 42.9105 55.1119 42.7655 54.9739C42.4645 54.6899 42.4525 54.2139 42.7375 53.9149L44.2655 52.3019C45.0745 51.4429 46.4395 51.4009 47.3025 52.2109L48.2605 53.1829C48.5275 53.4529 48.9615 53.4579 49.2295 53.1939C49.3305 53.0749 51.5085 50.4299 51.5085 50.4299C51.9225 49.9279 52.5065 49.6179 53.1555 49.5539C53.8055 49.4969 54.4365 49.6859 54.9395 50.0989C54.9825 50.1339 55.0215 50.1679 57.2175 52.4229C57.5065 52.7189 57.5015 53.1939 57.2045 53.4829C56.9085 53.7739 56.4325 53.7649 56.1435 53.4689C56.1435 53.4689 54.0945 51.3659 53.9485 51.2239C53.7935 51.0969 53.5445 51.0229 53.2995 51.0469C53.0505 51.0719 52.8265 51.1909 52.6675 51.3839C50.3435 54.2029 50.3155 54.2299 50.2775 54.2669C49.4195 55.1089 48.0345 55.0949 47.1915 54.2349C47.1915 54.2349 46.2615 53.2909 46.2455 53.2719C46.0145 53.0579 45.6025 53.0719 45.3555 53.3329L43.8255 54.9459C43.6775 55.1019 43.4795 55.1799 43.2815 55.1799Z"
fill="#AFB1B6"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M46.5577 46.1289C46.0047 46.1289 45.5547 46.5789 45.5547 47.1329C45.5547 47.6869 46.0047 48.1379 46.5587 48.1379C47.1127 48.1379 47.5637 47.6869 47.5637 47.1329C47.5637 46.5799 47.1127 46.1299 46.5577 46.1289ZM46.5587 49.6379C45.1777 49.6379 44.0547 48.5139 44.0547 47.1329C44.0547 45.7519 45.1777 44.6289 46.5587 44.6289C47.9407 44.6299 49.0637 45.7539 49.0637 47.1329C49.0637 48.5139 47.9397 49.6379 46.5587 49.6379Z"
fill="#AFB1B6"
/>
<rect x="1" y="1" width="98" height="98" rx="49" stroke="#AFB1B6" stroke-width="2" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

26
src/lib/svg/Logout.svelte Normal file
View File

@ -0,0 +1,26 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g filter="url(#filter0_i_1625_3311)">
<rect width="30" height="30" fill="url(#pattern0)" />
</g>
<defs>
<filter id="filter0_i_1625_3311" x="0" y="0" width="30" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<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 dx="61" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0.258824 0 0 0 0 0.388235 0 0 0 0 0.921569 0 0 0 1 0" />
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1625_3311" />
</filter>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_1625_3311" transform="scale(0.0104167)" />
</pattern>
<image
id="image0_1625_3311"
width="96"
height="96"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABmJLR0QA/wD/AP+gvaeTAAADqUlEQVR4nO2dvU8UQRyGH40KjYI22FnYGDpI7kSxM/gRjQmNBUGtLKz8+JNMsLAjojEaYwMROaWy0kRtjFbG00SjhVgMG+C8u5253ZnffbxPMoHA7s7c+8DezOzeDgghhBBCCJGcXQX3HwcmgDFgb/HmdMQf4AWwalR/coaA28A7YKOLykNgJOLr7gqqwEfsw25VluK9dHtmgV/Yh5xXqrECiMFuz+2qwD1gOGJbyuKUdQNC8BEwDNynN8IH91/QM/gIuAEcid2QElmxbkDZdFtvp115ECkDM8axDzUk/J7rhu7J+f2ExzH+4t6gPxVvTkd8wZ12akb1FyJPwGGPYywAV0toy0CS9ya83+MY78toyKDiOw4QkZAAYyTAGAkwRgKMkQBjJMAYCTBGAozJm4oYNKaAk5vfrwAvY1coAY4DuAnFCw0/XwLmgXqsinUKcjQLH+Ai8BQYjVWxBMBxmoefUQGeEEmCBMC0xzbRJEiAP1EkSAAsB2xbugQJgDXCLuaXKkECHFcIu6ZcmgQJcNSBGcIGXhXgGXCoSMUSsEUdOEuYhEncOKFjCRKwk0zCWsA+hSRIwP/UgTMkkiABzUkmQQJak0SCxWzoFHAC2GdQdyc8xwXrm9Uk8Bgn71vexikFjOJmHc8nrNOKbJwwQ85UdspT0KCEn1EB7uZtlErAFIMVfsYlnIiWpBLgM+Xbr7R97eoFGZNKQN99biuAttPdqQSsAo8S1dVNLAKv2m2Q8hQ0h3ucwKBQA67lbZRyHFDH3WVQxb0xjSWsuwgHgeuEPdikhhuI5d7OYjESXiNseG/JCG5AFRL+OnAOj1EwqBfUjiz8kGdPrONGv199d5CA5iQJHySgGcnCBwloJGn4IAHbSR4+SEDGCO5CSkj4NeA0BcIHCchYIGfWsoGsn+/V1WyHBOTfHd1IaeGDBEDYVHmp4YMEhFB6+CAB4Hd3dJTwQQIg/+7oaOGDBGTM01zCIq6fHyV80KckM76zdQF9GvcMumXgdeyKJWAnNRI/e06nIGMkwBgJMEYCjJEAYyTAGAkwRgKMkQBj8gT88DjG0TIaMqjkTUV89jjG3ObXNwXb0il9vY5YLy3g0LfriL3FPlzf0pfriN3CPtiQ0lPriPkwBHzAPljfcidODHHw6Yb+Bi7jVtHrBTasGxCLWeAn9n/hA3cK2k4Ft2aMdcitSt+tI9aMIeAm3dc76sl1xIou6HwM93CKMew+89XT64gJIYQQQghhwD+GY3D/XU+PSgAAAABJRU5ErkJggg=="
/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

8
src/lib/svg/Point.svelte Normal file
View File

@ -0,0 +1,8 @@
<svg width="20" height="26" viewBox="0 0 20 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.479126 9.35335C0.479126 4.19335 4.65246 0.0200195 9.8125 0.0200195C14.9725 0.0200195 19.1458 4.19335 19.1458 9.35335C19.1458 14.9133 13.2525 22.58 10.8391 25.5C10.3058 26.14 9.33246 26.14 8.79913 25.5C6.37246 22.58 0.479126 14.9133 0.479126 9.35335ZM6.47913 9.35335C6.47913 11.1933 7.97246 12.6867 9.8125 12.6867C11.6525 12.6867 13.1458 11.1933 13.1458 9.35335C13.1458 7.51335 11.6525 6.02002 9.8125 6.02002C7.97246 6.02002 6.47913 7.51335 6.47913 9.35335Z"
fill="#4263EB"
/>
</svg>

After

Width:  |  Height:  |  Size: 641 B

View File

@ -0,0 +1,19 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.7086 7.08398C9.90441 7.08398 9.25024 7.73815 9.25024 8.54315C9.25024 9.34732 9.90441 10.0007 10.7086 10.0007C11.5127 10.0007 12.1669 9.34732 12.1669 8.54315C12.1669 7.73815 11.5127 7.08398 10.7086 7.08398ZM10.7086 11.2507C9.21524 11.2507 8.00024 10.0365 8.00024 8.54315C8.00024 7.04898 9.21524 5.83398 10.7086 5.83398C12.2019 5.83398 13.4169 7.04898 13.4169 8.54315C13.4169 10.0365 12.2019 11.2507 10.7086 11.2507Z"
fill="#61646B"
/>
<mask id="mask0_1624_3626" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="3" y="1" width="15" height="17">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.83325 1.66699H17.5828V17.917H3.83325V1.66699Z" fill="white" />
</mask>
<g mask="url(#mask0_1624_3626)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.7078 2.91699C7.6061 2.91699 5.08276 5.46449 5.08276 8.59449C5.08276 12.577 9.76943 16.457 10.7078 16.6637C11.6461 16.4562 16.3328 12.5762 16.3328 8.59449C16.3328 5.46449 13.8094 2.91699 10.7078 2.91699ZM10.7078 17.917C9.21276 17.917 3.83276 13.2903 3.83276 8.59449C3.83276 4.77449 6.91693 1.66699 10.7078 1.66699C14.4986 1.66699 17.5828 4.77449 17.5828 8.59449C17.5828 13.2903 12.2028 17.917 10.7078 17.917Z"
fill="#61646B"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,8 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M20 10C20 12.7625 17.7625 15 15 15C12.2375 15 10 12.7625 10 10C10 7.2375 12.2375 5 15 5C17.7625 5 20 7.2375 20 10ZM5 22.5C5 19.175 11.6625 17.5 15 17.5C18.3375 17.5 25 19.175 25 22.5V23.75C25 24.4375 24.4375 25 23.75 25H6.25C5.5625 25 5 24.4375 5 23.75V22.5Z"
fill="#4263EB"
/>
</svg>

After

Width:  |  Height:  |  Size: 440 B

22
src/lib/svg/Search.svelte Normal file
View File

@ -0,0 +1,22 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_1648_1316" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="17" height="17">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.166626 0.666504H16.3973V16.8973H0.166626V0.666504Z" fill="white"/>
</mask>
<g mask="url(#mask0_1648_1316)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.28234 1.9165C4.4965 1.9165 1.4165 4.99567 1.4165 8.7815C1.4165 12.5673 4.4965 15.6473 8.28234 15.6473C12.0673 15.6473 15.1473 12.5673 15.1473 8.7815C15.1473 4.99567 12.0673 1.9165 8.28234 1.9165ZM8.28234 16.8973C3.80734 16.8973 0.166504 13.2565 0.166504 8.7815C0.166504 4.3065 3.80734 0.666504 8.28234 0.666504C12.7573 0.666504 16.3973 4.3065 16.3973 8.7815C16.3973 13.2565 12.7573 16.8973 8.28234 16.8973Z" fill="#61646B"/>
</g>
<mask id="mask1_1648_1316" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="12" y="13" width="6" height="5">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.8666 13.7559H17.0533V17.9348H12.8666V13.7559Z" fill="white"/>
</mask>
<g mask="url(#mask1_1648_1316)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4285 17.9348C16.2693 17.9348 16.1093 17.874 15.9868 17.7523L13.0501 14.824C12.806 14.5798 12.8051 14.184 13.0493 13.9398C13.2926 13.694 13.6885 13.6957 13.9335 13.9382L16.8701 16.8673C17.1143 17.1115 17.1151 17.5065 16.871 17.7507C16.7493 17.874 16.5885 17.9348 16.4285 17.9348Z" fill="#61646B"/>
</g>
</svg>
<style>
svg{
height: 100%;
width: auto;
}
</style>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,20 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 24C0 10.7452 10.7452 0 24 0C37.2548 0 48 10.7452 48 24C48 37.2548 37.2548 48 24 48C10.7452 48 0 37.2548 0 24Z" fill="#F3F4F6" />
<mask id="mask0_1574_3130" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="14" y="14" width="20" height="21">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 14.001H33.2849V34.722H14V14.001Z" fill="white" />
</mask>
<g mask="url(#mask0_1574_3130)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M19.2022 29.441C19.4312 29.441 19.6602 29.47 19.8842 29.53C20.5602 29.712 21.1472 30.164 21.4952 30.771C21.7212 31.152 21.8462 31.597 21.8502 32.051C21.8502 32.701 22.3722 33.222 23.0142 33.222H24.2672C24.9062 33.222 25.4282 32.704 25.4312 32.065C25.4272 31.359 25.7032 30.688 26.2082 30.183C26.7062 29.685 27.4022 29.386 28.0982 29.406C28.5542 29.417 28.9932 29.54 29.3802 29.76C29.9372 30.079 30.6482 29.889 30.9702 29.339L31.6342 28.232C31.7822 27.977 31.8252 27.657 31.7462 27.362C31.6682 27.067 31.4722 26.811 31.2082 26.66C30.5902 26.304 30.1492 25.73 29.9662 25.042C29.7852 24.367 29.8842 23.63 30.2372 23.023C30.4672 22.623 30.8042 22.286 31.2082 22.054C31.7502 21.737 31.9402 21.028 31.6252 20.476C31.6122 20.454 31.6002 20.431 31.5902 20.407L31.0042 19.391C30.6852 18.836 29.9752 18.645 29.4182 18.962C28.8162 19.318 28.1002 19.42 27.4122 19.239C26.7252 19.061 26.1492 18.626 25.7902 18.012C25.5602 17.628 25.4352 17.181 25.4312 16.726C25.4402 16.384 25.3202 16.077 25.1022 15.852C24.8852 15.628 24.5802 15.501 24.2672 15.501H23.0142C22.7042 15.501 22.4142 15.622 22.1952 15.84C21.9772 16.059 21.8582 16.35 21.8602 16.66C21.8392 18.122 20.6442 19.299 19.1972 19.299C18.7332 19.294 18.2862 19.169 17.8982 18.937C17.3532 18.627 16.6412 18.818 16.3222 19.373L15.6452 20.486C15.3352 21.024 15.5252 21.735 16.0772 22.056C16.8962 22.53 17.4072 23.414 17.4072 24.362C17.4072 25.31 16.8962 26.193 16.0752 26.668C15.5262 26.986 15.3362 27.693 15.6542 28.243L16.2852 29.331C16.4412 29.612 16.6962 29.815 16.9912 29.898C17.2852 29.98 17.6092 29.945 17.8792 29.795C18.2762 29.562 18.7382 29.441 19.2022 29.441ZM24.2672 34.722H23.0142C21.5452 34.722 20.3502 33.528 20.3502 32.059C20.3482 31.878 20.2962 31.69 20.1992 31.527C20.0422 31.253 19.7882 31.057 19.4952 30.979C19.2042 30.901 18.8852 30.944 18.6232 31.096C17.9952 31.446 17.2562 31.531 16.5802 31.341C15.9052 31.15 15.3222 30.686 14.9802 30.071L14.3552 28.994C13.6242 27.726 14.0592 26.101 15.3252 25.369C15.6842 25.162 15.9072 24.776 15.9072 24.362C15.9072 23.948 15.6842 23.561 15.3252 23.354C14.0582 22.618 13.6242 20.989 14.3542 19.721L15.0322 18.608C15.7532 17.354 17.3832 16.912 18.6542 17.642C18.8272 17.745 19.0152 17.797 19.2062 17.799C19.8292 17.799 20.3502 17.285 20.3602 16.653C20.3562 15.956 20.6312 15.287 21.1322 14.782C21.6352 14.278 22.3032 14.001 23.0142 14.001H24.2672C24.9832 14.001 25.6792 14.295 26.1782 14.806C26.6762 15.32 26.9512 16.025 26.9302 16.74C26.9322 16.901 26.9852 17.087 27.0812 17.25C27.2402 17.52 27.4912 17.71 27.7892 17.788C28.0872 17.862 28.3992 17.822 28.6642 17.665C29.9442 16.934 31.5732 17.372 32.3042 18.642L32.9272 19.721C32.9432 19.75 32.9572 19.778 32.9692 19.807C33.6312 21.058 33.1892 22.633 31.9592 23.352C31.7802 23.455 31.6352 23.599 31.5352 23.773C31.3802 24.042 31.3372 24.362 31.4152 24.656C31.4952 24.956 31.6862 25.205 31.9552 25.359C32.5622 25.708 33.0152 26.296 33.1962 26.975C33.3772 27.653 33.2782 28.389 32.9252 28.996L32.2612 30.102C31.5302 31.358 29.9012 31.793 28.6342 31.061C28.4652 30.964 28.2702 30.911 28.0762 30.906H28.0702C27.7812 30.906 27.4842 31.029 27.2682 31.244C27.0492 31.463 26.9292 31.755 26.9312 32.065C26.9242 33.534 25.7292 34.722 24.2672 34.722Z"
fill="black"
/>
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M23.6452 22.4746C22.6052 22.4746 21.7592 23.3216 21.7592 24.3616C21.7592 25.4016 22.6052 26.2466 23.6452 26.2466C24.6852 26.2466 25.5312 25.4016 25.5312 24.3616C25.5312 23.3216 24.6852 22.4746 23.6452 22.4746ZM23.6452 27.7466C21.7782 27.7466 20.2592 26.2286 20.2592 24.3616C20.2592 22.4946 21.7782 20.9746 23.6452 20.9746C25.5122 20.9746 27.0312 22.4946 27.0312 24.3616C27.0312 26.2286 25.5122 27.7466 23.6452 27.7466Z"
fill="black"
/>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,8 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M24.3777 15C24.3777 15.425 24.3402 15.825 24.2902 16.225L26.9277 18.2875C27.1652 18.475 27.2277 18.8125 27.0777 19.0875L24.5777 23.4125C24.4277 23.6875 24.1027 23.8 23.8152 23.6875L20.7027 22.4375C20.0527 22.925 19.3527 23.35 18.5902 23.6625L18.1152 26.975C18.0777 27.275 17.8152 27.5 17.5027 27.5H12.5027C12.1902 27.5 11.9277 27.275 11.8902 26.975L11.4152 23.6625C10.6527 23.35 9.95273 22.9375 9.30273 22.4375L6.19023 23.6875C5.91523 23.7875 5.57773 23.6875 5.42773 23.4125L2.92773 19.0875C2.77773 18.8125 2.84023 18.475 3.07773 18.2875L5.71523 16.225C5.66523 15.825 5.62773 15.4125 5.62773 15C5.62773 14.5875 5.66523 14.175 5.71523 13.775L3.07773 11.7125C2.84023 11.525 2.76523 11.1875 2.92773 10.9125L5.42773 6.5875C5.57773 6.3125 5.90273 6.2 6.19023 6.3125L9.30273 7.5625C9.95273 7.075 10.6527 6.65 11.4152 6.3375L11.8902 3.025C11.9277 2.725 12.1902 2.5 12.5027 2.5H17.5027C17.8152 2.5 18.0777 2.725 18.1152 3.025L18.5902 6.3375C19.3527 6.65 20.0527 7.0625 20.7027 7.5625L23.8152 6.3125C24.0902 6.2125 24.4277 6.3125 24.5777 6.5875L27.0777 10.9125C27.2277 11.1875 27.1652 11.525 26.9277 11.7125L24.2902 13.775C24.3402 14.175 24.3777 14.575 24.3777 15ZM10.6277 15C10.6277 17.4125 12.5902 19.375 15.0027 19.375C17.4152 19.375 19.3777 17.4125 19.3777 15C19.3777 12.5875 17.4152 10.625 15.0027 10.625C12.5902 10.625 10.6277 12.5875 10.6277 15Z"
fill="#4263EB"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

26
src/lib/svg/Share.svelte Normal file
View File

@ -0,0 +1,26 @@
<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g filter="url(#filter0_i_956_444)">
<rect width="42" height="42" fill="url(#share_svelteSvgPictureid)" />
</g>
<defs>
<filter id="filter0_i_956_444" x="0" y="0" width="42" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<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 dx="436" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
<feColorMatrix type="matrix" values="0 0 0 0 0.258824 0 0 0 0 0.388235 0 0 0 0 0.921569 0 0 0 1 0" />
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_956_444" />
</filter>
<pattern id="share_svelteSvgPictureid" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_956_444" transform="scale(0.00520833)" />
</pattern>
<image
id="image0_956_444"
width="192"
height="192"
xlink:href="data:image/png;base64,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"
/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

6
src/lib/svg/Star.svelte Normal file
View File

@ -0,0 +1,6 @@
<svg width="27" height="26" viewBox="0 0 27 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M13.5 21.2792L19.725 25.0442C20.865 25.7342 22.26 24.7142 21.96 23.4242L20.31 16.3442L25.815 11.5742C26.82 10.7042 26.28 9.05416 24.96 8.94916L17.715 8.33416L14.88 1.64416C14.37 0.42916 12.63 0.42916 12.12 1.64416L9.28499 8.31916L2.03999 8.93416C0.719987 9.03916 0.179987 10.6892 1.18499 11.5592L6.68999 16.3292L5.03999 23.4092C4.73999 24.6992 6.13499 25.7192 7.27499 25.0292L13.5 21.2792Z"
fill="#FFBF00"
/>
</svg>

After

Width:  |  Height:  |  Size: 527 B

Some files were not shown because too many files have changed in this diff Show More