feat: improved patches page ui

This commit is contained in:
afn
2022-12-25 16:56:38 -05:00
parent c26c2a5783
commit 6f6f30e3bb
15 changed files with 314 additions and 155 deletions

View File

@@ -34,23 +34,24 @@ body {
}
:root {
/* TODO properly name these */
--main-font: "Manrope", sans-serif;
--mono-font: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
--white: hsl(206, 100%, 94%);
--accent-color: hsl(206, 100%, 81%);
--accent-color-two: hsl(208, 28%, 82%);
--accent-low-opacity: hsla(205, 100%, 81%, 0.1);
--accent-color-two: hsl(208, 75%, 82%);
--accent-low-opacity: hsla(205, 100%, 81%, 0.15);
--bg-color: hsl(252, 10%, 11%);
--grey-one: hsl(210, 14%, 17%);
--grey-two: hsl(212, 19%, 19%);
--grey-three: hsl(221, 17%, 26%);
--grey-four: hsl(226, 48%, 18%);
--grey-five: hsl(208, 30%, 75%);
--grey-six: hsl(230, 7%, 13%);
--grey-six: hsl(230, 9%, 13%);
--grey-seven: hsl(240, 9%, 13.5%);
--grey-eight: hsla(207, 30%, 75%, 0.577);
--grey-nine: hsla(240, 6%, 7%, 0.3);
--grey-ten: hsl(230, 9.5%, 17%);
--grey-ten: hsl(230, 9.5%, 17.5%);
--bezier-one: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
@@ -144,9 +145,9 @@ p {
hr {
display: block;
height: 1px;
height: 2px;
border: 0;
border-top: 1px solid var(--grey-three);
border-top: 2px solid var(--grey-three);
}
input {

View File

@@ -59,7 +59,7 @@
@media (max-width: 768px) {
li {
padding: 1rem 1.25rem;
padding: 0.75rem 1.25rem;
text-align: left;
justify-content: left;
border-radius: 100px;

View File

@@ -97,31 +97,34 @@
</button>
</div>
</div>
<div class="overlay" transition:fade={{ duration: 350 }} />
<div
class="overlay"
transition:fade={{ duration: 350 }}
on:click={() => (menuOpen = !menuOpen)}
on:keypress={() => (menuOpen = !menuOpen)}
/>
</div>
{/if}
</nav>
<!-- settings -->
<Modal bind:modalOpen>
<div class="settings-menu">
<h3>Settings</h3>
<p>Configure the website's API. Defaults to ReVanced.</p>
<div id="settings-content">
<div class="input-wrapper">
<input name="api-url" type="text" bind:value={url} />
<button id="button-reset" on:click={reset}>
<Svg viewBoxHeight={48} svgHeight={24}>
<path
d="M11.2 36.725C14.6667 40.2417 18.8833 42 23.85 42C26.35 42 28.7 41.525 30.9 40.575C33.1 39.625 35.025 38.3333 36.675 36.7C38.325 35.0667 39.625 33.15 40.575 30.95C41.525 28.75 42 26.4 42 23.9C42 21.4 41.525 19.0667 40.575 16.9C39.625 14.7333 38.325 12.8417 36.675 11.225C35.025 9.60833 33.1 8.33333 30.9 7.4C28.7 6.46667 26.35 6 23.85 6C21.1833 6 18.6583 6.58333 16.275 7.75C13.8917 8.91667 11.8333 10.5167 10.1 12.55V7.25H7.1V17.65H17.55V14.65H12.3C13.7667 12.95 15.4917 11.5833 17.475 10.55C19.4583 9.51667 21.5833 9 23.85 9C28.0167 9 31.5833 10.425 34.55 13.275C37.5167 16.125 39 19.6167 39 23.75C39 27.9833 37.5333 31.5833 34.6 34.55C31.6667 37.5167 28.0833 39 23.85 39C19.6833 39 16.1667 37.5333 13.3 34.6C10.4333 31.6667 9 28.1167 9 23.95H6C6 28.95 7.73333 33.2083 11.2 36.725Z"
/>
</Svg>
</button>
</div>
<div class="buttons">
<Button kind="tertiary" on:click={clear_and_reload}>Clear cache</Button>
<Button kind="tertiary" on:click={save}>Save</Button>
</div>
<svelte:fragment slot="title">Settings</svelte:fragment>
<svelte:fragment slot="description">Configure the website's API. Defaults to ReVanced.</svelte:fragment>
<div id="settings-content">
<div class="input-wrapper">
<input name="api-url" type="text" bind:value={url} />
<button id="button-reset" on:click={reset}>
<Svg viewBoxHeight={48} svgHeight={24}>
<path
d="M11.2 36.725C14.6667 40.2417 18.8833 42 23.85 42C26.35 42 28.7 41.525 30.9 40.575C33.1 39.625 35.025 38.3333 36.675 36.7C38.325 35.0667 39.625 33.15 40.575 30.95C41.525 28.75 42 26.4 42 23.9C42 21.4 41.525 19.0667 40.575 16.9C39.625 14.7333 38.325 12.8417 36.675 11.225C35.025 9.60833 33.1 8.33333 30.9 7.4C28.7 6.46667 26.35 6 23.85 6C21.1833 6 18.6583 6.58333 16.275 7.75C13.8917 8.91667 11.8333 10.5167 10.1 12.55V7.25H7.1V17.65H17.55V14.65H12.3C13.7667 12.95 15.4917 11.5833 17.475 10.55C19.4583 9.51667 21.5833 9 23.85 9C28.0167 9 31.5833 10.425 34.55 13.275C37.5167 16.125 39 19.6167 39 23.75C39 27.9833 37.5333 31.5833 34.6 34.55C31.6667 37.5167 28.0833 39 23.85 39C19.6833 39 16.1667 37.5333 13.3 34.6C10.4333 31.6667 9 28.1167 9 23.95H6C6 28.95 7.73333 33.2083 11.2 36.725Z"
/>
</Svg>
</button>
</div>
<div class="buttons">
<Button kind="tertiary" on:click={clear_and_reload}>Clear cache</Button>
<Button kind="tertiary" on:click={save}>Save</Button>
</div>
</div>
</Modal>
@@ -144,14 +147,6 @@
align-items: center;
}
.settings-menu h3 {
text-align: center;
margin-bottom: 1rem;
}
.settings-menu p {
margin-bottom: 1.25rem;
}
.input-wrapper {
margin-bottom: 0.75rem;
display: flex;
@@ -207,7 +202,7 @@
a {
display: flex;
}
img {
height: 22px;
}
@@ -228,7 +223,7 @@
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3);
background-color: rgba(0, 0, 0, 0.5);
z-index: 88;
}

View File

@@ -0,0 +1,50 @@
<script>
export let dropdown = false;
export let check = false;
export let selected = false;
</script>
<button class:selected on:click>
{#if check}
<img id="check" src="/icons/check.svg" alt="selected" />
{/if}
<slot />
{#if dropdown}
<img id="dropdown" src="/icons/arrow.svg" alt="dropdown" />
{/if}
</button>
<style>
button {
font-family: var(--font-two);
border: none;
border: 1.5px solid var(--grey-three);
background-color: transparent;
color: var(--grey-five);
height: 32px;
padding: 0 16px;
border-radius: 8px;
font-size: 0.85rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
.selected {
background-color: var(--accent-low-opacity);
color: var(--accent-color);
}
img {
height: 18px;
}
#dropdown {
margin-right: -6px;
}
#check {
margin-left: -6px;
}
</style>

View File

@@ -17,7 +17,19 @@
aria-modal="true"
transition:fade={{ easing: quadInOut, duration: 250 }}
>
<slot />
<div class="title">
<h3>
<slot name="title" />
</h3>
</div>
{#if $$slots.description}
<p>
<slot name="description" />
</p>
{/if}
<div class="slot"><slot /></div>
</div>
{/if}
@@ -32,13 +44,30 @@
z-index: 999;
}
h3 {
text-align: center;
}
.title {
position: sticky;
padding-top: 46px;
padding-bottom: 20px;
top: 0;
width: 100%;
background-color: var(--grey-six);
}
p {
margin-bottom: 1rem;
}
.modal {
position: fixed;
width: min(85%, 425px);
max-height: 75%;
overflow-y: scroll;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 46px 36px;
border-radius: 26px;
background-color: var(--grey-six);
display: flex;
@@ -52,4 +81,12 @@
box-shadow: 0px 4px 5px 0px rgba(0, 0, 0, 0.14), 0px 1px 10px 0px rgba(0, 0, 0, 0.12),
0px 2px 4px -1px rgba(0, 0, 0, 0.2);
}
.slot {
padding: 0px 28px 36px 28px;
}
.modal::-webkit-scrollbar {
display: none;
}
</style>

View File

@@ -11,7 +11,7 @@
}
</script>
<div>
<div class="search-container">
<img src="../icons/search.svg" id="search" alt="Search" />
{#if searchTerm}
<img
@@ -37,50 +37,51 @@
/* umm dont ask */
position: absolute;
z-index: 1;
left: 23px;
top: 52px;
left: 16px;
top: 14px;
height: 24px;
}
#clear {
position: absolute;
right: 42px;
top: 52px;
right: 16px;
top: 14px;
z-index: 1;
height: 24px;
cursor: pointer;
}
.clear {
padding-right: 2.5rem;
.search-container {
position: relative;
}
input {
position: relative;
display: flex;
padding: 1rem 3rem;
padding: 1rem 3.25rem;
width: 100%;
color: var(--accent-color-two);
font-weight: 500;
font-size: 0.92rem;
border-radius: 100px;
border: none;
background-color: var(--grey-ten);
}
input::placeholder {
color: var(--grey-eight);
font-size: 0.9rem;
color: var(--grey-five);
font-size: 0.92rem;
font-weight: 500;
transition: all 0.2s var(--bezier-one);
}
@media (max-width: 768px) {
#search {
left: 26px;
top: 38px;
}
input:focus {
outline: none;
}
#clear {
right: 26px;
top: 38px;
}
input {
background-color: var(--grey-ten);
border: none;
border-radius: 100px;
}
input:focus::placeholder {
outline: none;
color: var(--accent-color)
}
</style>

View File

@@ -1,7 +1,10 @@
<script>
import { fade } from "svelte/transition";
import { fade } from 'svelte/transition';
</script>
<div class="spinner" transition:fade={{duration: 250}}/>
<div class="spinner-container">
<div class="spinner" transition:fade={{ duration: 250 }} />
</div>
<style>
@keyframes spinner {
@@ -10,12 +13,16 @@
}
}
.spinner:before {
content: '';
box-sizing: border-box;
.spinner-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.spinner:before {
content: '';
box-sizing: border-box;
width: 50px;
height: 50px;
border-radius: 50%;

View File

@@ -53,8 +53,8 @@
}
img {
height: 48px;
width: 48px;
height: 42px;
width: 42px;
}
a {
width: max-content;

View File

@@ -48,6 +48,7 @@
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
background-color: var(--grey-six);
padding: 0.75rem 1.25rem;
border-bottom: 1px solid var(--grey-three);
@@ -97,7 +98,7 @@
.contrib-host {
padding: 0.75rem;
gap: 0.25rem;
grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
}
}
</style>

View File

@@ -5,11 +5,13 @@
import type { Patch } from '$lib/types';
import { patches as api_patches } from '$data/api';
import TreeMenu from '$lib/components/TreeView/TreeMenu.svelte';
import TreeMenuButton from '$lib/components/TreeView/TreeMenuButton.svelte';
import PackageMenu from './PackageMenu.svelte';
import Package from './Package.svelte';
import PatchItem from './PatchItem.svelte';
import Footer from '$layout/Footer.svelte';
import Search from '$lib/components/Search.svelte';
import FilterChip from '$lib/components/FilterChip.svelte';
import Modal from '$lib/components/Modal.svelte';
$: ({ patches, packages } = $api_patches);
@@ -17,6 +19,7 @@
let searchTerm: string;
let searchTermFiltered: string;
let timeout: any = null;
let mobilePackages = false;
function filterByPackage(selectedPkg: string | boolean, packageList: any) {
for (let i = 0; i < packageList.length; i++) {
@@ -63,18 +66,57 @@
<meta content="ReVanced | Patches" name="twitter:title" />
</svelte:head>
<div class="search">
<div class="search-contain">
<!-- Must bind both variables: we get searchTerm from the text input, -->
<!-- and searchTermFiltered gets cleared with the clear button -->
<Search
bind:searchTerm
bind:searchTermFiltered
title="Search for patches"
on:keyup={debounce}
/>
<div class="filter-chips">
<FilterChip
selected={selectedPkg}
dropdown
on:click={() => (mobilePackages = !mobilePackages)}
>
{selectedPkg ? selectedPkg : 'Packages'}
</FilterChip>
<!-- <FilterChip check>Universal</FilterChip>
<FilterChip>Patch options</FilterChip> -->
</div>
<div class="mobile-packages-modal">
<Modal bind:modalOpen={mobilePackages}>
<svelte:fragment slot="title">Packages</svelte:fragment>
<div class="mobile-packages">
<!-- <span on:click={() => (mobilePackages = !mobilePackages)}>
<Package bind:selectedPkg name="All packages" />
</span> -->
{#each packages as pkg}
<span
on:click={() => (mobilePackages = !mobilePackages)}
on:keypress={() => (mobilePackages = !mobilePackages)}
>
<Package bind:selectedPkg name={pkg} />
</span>
{/each}
</div>
</Modal>
</div>
</div>
</div>
<main>
<aside in:fly={{ y: 10, easing: quintOut, duration: 750 }}>
<TreeMenu>
<!-- Must bind both variables: we get searchTerm from the text input, -->
<!-- and searchTermFiltered gets cleared with the clear button -->
<Search bind:searchTerm bind:searchTermFiltered title="Search patches" on:keyup={debounce} />
<PackageMenu>
<span class="packages">
{#each packages as pkg}
<TreeMenuButton bind:selectedPkg name={pkg} />
<Package bind:selectedPkg name={pkg} />
{/each}
</span>
</TreeMenu>
</PackageMenu>
</aside>
<div class="patches-container">
@@ -98,15 +140,28 @@
<style>
main {
margin-inline: auto;
display: grid;
grid-template-columns: 300px 3fr;
width: min(98%, 82rem);
width: min(90%, 80rem);
margin-inline: auto;
gap: 1.5rem;
}
.search {
padding-top: 5rem;
padding-bottom: 1.25rem;
background-color: var(--grey-seven);
}
.search-contain {
width: min(90%, 80rem);
margin-inline: auto;
}
.patches-container {
margin-top: 6.7rem;
overflow: hidden;
border-radius: 20px;
margin-top: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
@@ -114,36 +169,48 @@
position: sticky;
z-index: 1;
min-height: calc(100vh - 6rem);
padding-bottom: 3rem;
padding-right: 0.75rem;
margin-bottom: 3rem;
}
.filter-chips {
display: none;
}
.mobile-packages {
margin-bottom: -1px;
overflow: hidden;
border-radius: 12px;
border: 1px solid var(--grey-three);
}
@media (min-width: 768px) {
.mobile-packages-modal {
display: none;
}
}
@media (max-width: 768px) {
main {
grid-template-columns: none;
flex-direction: column;
margin-top: 4rem;
gap: 0;
}
aside {
display: none;
}
.patches-container {
padding-left: 0.75rem;
padding-bottom: 1.25rem;
margin-top: 0;
margin-top: 1.5rem;
margin-bottom: 1.5rem;
gap: 0.75rem;
}
.packages {
.filter-chips {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 2.75rem;
gap: 0.5rem;
overflow-x: scroll;
}
.packages::-webkit-scrollbar {
display: none;
margin-top: 1rem;
gap: 0.75rem;
padding-bottom: 0rem;
}
}
</style>

View File

@@ -19,9 +19,10 @@
<style>
.package {
padding: 0.6rem 1rem;
font-size: 0.9rem;
border-radius: 8px;
padding: 0.75rem 1rem;
font-size: 0.85rem;
font-weight: 500;
border-radius: 100px;
cursor: pointer;
display: flex;
align-items: center;
@@ -29,20 +30,14 @@
width: 100%;
user-select: none;
transition: background-color 0.4s var(--bezier-one);
}
.package{
color: var(--grey-five);
transition: color 0.3s var(--bezier-one);
}
.selected {
color: var(--grey-four);
color: var(--accent-color);
transition: color 0.3s var(--bezier-one);
}
.selected {
background-color: var(--accent-color);
background-color: var(--accent-low-opacity);
}
.package:hover:not(.selected) {
background-color: var(--grey-six);
@@ -54,22 +49,23 @@
@media (max-width: 768px) {
.package {
border-radius: 12px;
padding: 0.5rem 1rem;
width: max-content;
border-radius: 0px;
font-size: 0.9rem;
padding: 1rem 1rem;
width: 100%;
background-color: transparent;
border: 1px solid var(--grey-three);
}
.package{
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
color: var(--accent-color);
color: var(--grey-five);
border-bottom: 1px solid var(--grey-three);
}
.selected {
color: var(--accent-color);
background-color: var(--accent-low-opacity);
}
.package:not(.selected):hover {
color: var(--grey-five);
}

View File

@@ -1,4 +1,6 @@
<div class="menu">
<h6>PACKAGES</h6>
<hr/>
<div class="slot">
<slot />
</div>
@@ -12,8 +14,8 @@
display: flex;
flex-direction: column;
position: sticky;
top: 70px;
padding-top: calc(6rem - 70px);
top: 60px;
padding-top: calc(6rem - 60px);
overflow-y: scroll;
}
@@ -33,11 +35,19 @@
word-break: break-all;
}
@media (max-width: 768px) {
h6 {
margin-bottom: 1rem;
color: var(--accent-color);
}
@media (max-width: 768px) {
.menu {
padding: 0.75rem;
height: unset;
}
h6, hr {
display: none;
}
}
</style>

View File

@@ -2,8 +2,7 @@
import { slide, fade } from 'svelte/transition';
import { quintOut } from 'svelte/easing';
import type { Patch } from '$lib/types';
import { friendlyName } from '$lib/utils'
import { friendlyName } from '$lib/utils';
export let patch: Patch;
const hasPatchOptions = !!patch.options.length;
@@ -26,37 +25,34 @@
{/if}
</div>
<h5>{patch.description}</h5>
<div class="info-container">
<ul class="info-container">
{#each patch.compatiblePackages as pkg, i}
<a
href="https://play.google.com/store/apps/details?id={pkg.name}"
target="_blank"
rel="noreferrer"
>
<h6 class="boxed">📦 {pkg.name}</h6>
<li class="patch-info">📦 {pkg.name} ·</li>
</a>
{/each}
<!-- should i hardcode this to get the version of the first package? idk you cant stop me -->
{#if patch.compatiblePackages.length && patch.compatiblePackages[0].versions.length}
<h6 class="boxed">
🎯 {patch.compatiblePackages[0].versions.slice(-1)}
</h6>
<li class="patch-info">
🎯 {patch.compatiblePackages[0].versions.slice(-1)} ·
</li>
{/if}
{#if !patch.compatiblePackages.length}
<h6 class="boxed">
🌎 Universal patch
</h6>
<li class="patch-info">🌎 Universal patch</li>
{/if}
<h6 class="boxed">🧩 {patch.version}</h6>
<li class="patch-info">🧩 {patch.version}</li>
{#if hasPatchOptions}
<h6 class="boxed">⚙️ Patch options</h6>
<li class="patch-info">· ⚙️ Patch options</li>
{/if}
</div>
</ul>
{#if expanded && hasPatchOptions}
<span transition:fade|local={{ easing: quintOut, duration: 1000 }}>
@@ -75,36 +71,39 @@
<style>
h3 {
margin-right: 0.5rem;
margin-bottom: 0.3rem;
}
h5 {
margin-bottom: 0.5rem;
}
h6 {
border-radius: 8px;
margin-bottom: 0.2rem;
color: var(--accent-color);
background-color: var(--grey-two);
padding: 0.2rem 0.4rem;
display: flex;
word-break: break-all;
}
#option-title {
color: var(--accent-color);
color: var(--accent-color-two);
}
.patch-info {
list-style: none;
font-size: 0.8rem;
font-weight: 500;
color: var(--grey-five);
}
a {
text-decoration: none;
}
a .patch-info:hover {
text-decoration: underline;
color: var(--accent-color-two);
text-decoration-style: wavy;
text-decoration-color: var(--accent-color-two);
}
.info-container {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
margin: 0.3rem 0rem;
width: 100%;
margin-top: 0.5rem;
}
.patch-container {
@@ -159,10 +158,4 @@
display: flex;
flex-direction: column;
}
@media (max-width: 768px) {
.patch-container {
border-radius: 16px;
}
}
</style>

1
static/icons/check.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M18.9 36.4 7 24.5l2.9-2.85 9 9L38.05 11.5l2.9 2.85Z" fill="#ACC1D2"/></svg>

After

Width:  |  Height:  |  Size: 147 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z" fill="#ACC0D3"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="m39.75 42.75-13.3-13.3Q25 30.65 23 31.35t-4.25.7q-5.65 0-9.525-3.9t-3.875-9.4q0-5.5 3.9-9.4 3.9-3.9 9.4-3.9 5.55 0 9.4 3.9 3.85 3.9 3.85 9.4 0 2.2-.65 4.15-.65 1.95-1.95 3.7l13.35 13.25ZM18.7 28.05q3.85 0 6.55-2.725 2.7-2.725 2.7-6.575t-2.7-6.575Q22.55 9.45 18.7 9.45q-3.95 0-6.675 2.725Q9.3 14.9 9.3 18.75t2.725 6.575Q14.75 28.05 18.7 28.05Z" fill="#ACC0D3"/></svg>

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 438 B