mirror of
https://github.com/ivabus/gui
synced 2025-06-07 15:50:27 +03:00
parent
b3731a136d
commit
9970b686bb
9 changed files with 217 additions and 91 deletions
|
@ -1,7 +1,26 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '$appcss';
|
import '$appcss';
|
||||||
import Placeholder from '$components/Placeholder/Placeholder.svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import type { Course } from '$libs/types';
|
||||||
|
|
||||||
|
import Gallery from '@tea/ui/Gallery/Gallery.svelte';
|
||||||
|
import { getFeaturedCourses } from '@api';
|
||||||
|
|
||||||
|
let courses: Course[] = [];
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (!courses.length) {
|
||||||
|
courses = await getFeaturedCourses();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Placeholder label="FeaturedCourses" />
|
<Gallery
|
||||||
<h1>test</h1>
|
title="FEATURED COURSES"
|
||||||
|
items={courses.map((course) => ({
|
||||||
|
title: course.title,
|
||||||
|
subTitle: course.sub_title,
|
||||||
|
imageUrl: course.banner_image_url,
|
||||||
|
link: course.link
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '$appcss';
|
import '$appcss';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { watchResize } from 'svelte-watch-resize';
|
|
||||||
import type { Package } from '@tea/ui/types';
|
import type { Package } from '@tea/ui/types';
|
||||||
import Preloader from '@tea/ui/Preloader/Preloader.svelte';
|
|
||||||
import FeaturedPackage from './FeaturedPackage.svelte';
|
import Gallery from '@tea/ui/Gallery/Gallery.svelte';
|
||||||
import {
|
import {
|
||||||
featuredPackages as featuredPackagesStore,
|
featuredPackages as featuredPackagesStore,
|
||||||
initializeFeaturedPackages
|
initializeFeaturedPackages
|
||||||
|
@ -12,82 +11,23 @@
|
||||||
|
|
||||||
let featuredPackages: Package[] = [];
|
let featuredPackages: Package[] = [];
|
||||||
|
|
||||||
let pkgFocus = 0;
|
|
||||||
let width = 0;
|
|
||||||
let styleFeaturedPackages: string;
|
|
||||||
|
|
||||||
function resetFeaturedStyle() {
|
|
||||||
const position = pkgFocus * width;
|
|
||||||
styleFeaturedPackages = `
|
|
||||||
width: ${featuredPackages.length * width}px;
|
|
||||||
left: -${position}px;
|
|
||||||
transition: left 0.6s ease-in;
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleContainerResize(node: HTMLElement) {
|
|
||||||
width = node.clientWidth;
|
|
||||||
resetFeaturedStyle();
|
|
||||||
}
|
|
||||||
|
|
||||||
let loop: NodeJS.Timer;
|
|
||||||
|
|
||||||
function resetLoop() {
|
|
||||||
if (loop) clearInterval(loop);
|
|
||||||
loop = setInterval(() => {
|
|
||||||
pkgFocus++;
|
|
||||||
if (pkgFocus === featuredPackages.length) {
|
|
||||||
pkgFocus = 0;
|
|
||||||
}
|
|
||||||
resetFeaturedStyle();
|
|
||||||
}, 3000);
|
|
||||||
resetFeaturedStyle();
|
|
||||||
}
|
|
||||||
|
|
||||||
featuredPackagesStore.subscribe((v) => {
|
featuredPackagesStore.subscribe((v) => {
|
||||||
featuredPackages = v;
|
featuredPackages = v;
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => clearInterval(loop));
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!featuredPackages.length) {
|
if (!featuredPackages.length) {
|
||||||
initializeFeaturedPackages();
|
initializeFeaturedPackages();
|
||||||
}
|
}
|
||||||
resetLoop();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="h-96 w-full bg-black" use:watchResize={handleContainerResize}>
|
<Gallery
|
||||||
<!-- <Placeholder label="FeaturedPackages" /> -->
|
title="FEATURED PACKAGES"
|
||||||
<header class="flex h-12 items-center justify-between bg-accent px-2">
|
items={featuredPackages.map((pkg) => ({
|
||||||
<p>FEATURED PACKAGES</p>
|
title: pkg.full_name,
|
||||||
<ul class="flex gap-2">
|
subTitle: pkg.maintainer || '',
|
||||||
{#each featuredPackages as pkg, i}
|
imageUrl: pkg.thumb_image_url,
|
||||||
<button
|
link: `/packages/${pkg.slug}`
|
||||||
on:click={() => {
|
}))}
|
||||||
pkgFocus = i;
|
|
||||||
resetLoop();
|
|
||||||
}}
|
|
||||||
class={`bg-purple h-4 w-4 rounded-lg border-2 border-white transition-colors ${
|
|
||||||
i === pkgFocus ? 'bg-purple-900' : ''
|
|
||||||
}`}
|
|
||||||
/>
|
/>
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
</header>
|
|
||||||
<figure class="absolute bottom-0 top-12 left-0 right-0 overflow-hidden">
|
|
||||||
{#if featuredPackages.length}
|
|
||||||
<section class="absolute top-0 flex h-full" style={styleFeaturedPackages}>
|
|
||||||
{#each featuredPackages as pkg}
|
|
||||||
<div class="h-full" style={`width:${width}px`}>
|
|
||||||
<a href={`/packages/${pkg.slug}`}>
|
|
||||||
<FeaturedPackage {pkg} {width} />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</section>
|
|
||||||
{:else}
|
|
||||||
<Preloader />
|
|
||||||
{/if}
|
|
||||||
</figure>
|
|
||||||
</section>
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* * make cors work with api.tea.xyz/v1
|
* * make cors work with api.tea.xyz/v1
|
||||||
*/
|
*/
|
||||||
import type { Package, Review } from '@tea/ui/types';
|
import type { Package, Review } from '@tea/ui/types';
|
||||||
import type { GUIPackage } from '../types';
|
import type { GUIPackage, Course } from '../types';
|
||||||
import { PackageStates } from '../types';
|
import { PackageStates } from '../types';
|
||||||
import { loremIpsum } from 'lorem-ipsum';
|
import { loremIpsum } from 'lorem-ipsum';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
@ -216,3 +216,28 @@ export async function installPackage(full_name: string) {
|
||||||
function delay(ms: number) {
|
function delay(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getFeaturedCourses(): Promise<Course[]> {
|
||||||
|
const mockCourses: Course[] = [
|
||||||
|
{
|
||||||
|
title: 'Developing With Tea',
|
||||||
|
sub_title: 'by Mxcl',
|
||||||
|
link: '#',
|
||||||
|
banner_image_url: 'https://tea.xyz/Images/packages/mesonbuild_com.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Brewing Tea',
|
||||||
|
sub_title: 'by Mxcl',
|
||||||
|
link: '#',
|
||||||
|
banner_image_url: 'https://tea.xyz/Images/packages/tea_xyz_gx_cc.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Harvesting Tea',
|
||||||
|
sub_title: 'by Mxcl',
|
||||||
|
link: '#',
|
||||||
|
banner_image_url: 'https://tea.xyz/Images/packages/ipfs_tech.jpg'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return mockCourses;
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Command } from '@tauri-apps/api/shell';
|
||||||
import { readDir, BaseDirectory } from '@tauri-apps/api/fs';
|
import { readDir, BaseDirectory } from '@tauri-apps/api/fs';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import type { Package, Review } from '@tea/ui/types';
|
import type { Package, Review } from '@tea/ui/types';
|
||||||
import type { GUIPackage } from '../types';
|
import type { GUIPackage, Course } from '../types';
|
||||||
import * as mock from './mock';
|
import * as mock from './mock';
|
||||||
import { PackageStates } from '../types';
|
import { PackageStates } from '../types';
|
||||||
|
|
||||||
|
@ -137,3 +137,8 @@ async function getInstalledPackages() {
|
||||||
});
|
});
|
||||||
return packages;
|
return packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getFeaturedCourses(): Promise<Course[]> {
|
||||||
|
const courses = await mock.getFeaturedCourses();
|
||||||
|
return courses;
|
||||||
|
}
|
||||||
|
|
|
@ -16,3 +16,10 @@ export type GUIPackage = Package & {
|
||||||
state: PackageStates;
|
state: PackageStates;
|
||||||
installed_version?: string;
|
installed_version?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Course = {
|
||||||
|
title: string;
|
||||||
|
sub_title: string;
|
||||||
|
banner_image_url: string;
|
||||||
|
link: string;
|
||||||
|
};
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/line-clamp": "^0.4.2"
|
"@tailwindcss/line-clamp": "^0.4.2",
|
||||||
|
"svelte-watch-resize": "^1.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
90
packages/ui/src/Gallery/Gallery.svelte
Normal file
90
packages/ui/src/Gallery/Gallery.svelte
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import '$appcss';
|
||||||
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
import { watchResize } from 'svelte-watch-resize';
|
||||||
|
import Preloader from '../Preloader/Preloader.svelte';
|
||||||
|
import GalleryItem from './GalleryItem.svelte';
|
||||||
|
|
||||||
|
export let title = '';
|
||||||
|
|
||||||
|
interface GalleryItemShape {
|
||||||
|
imageUrl: string;
|
||||||
|
title: string;
|
||||||
|
subTitle: string;
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let items: GalleryItemShape[] = [];
|
||||||
|
|
||||||
|
let focus = 0;
|
||||||
|
let width = 0;
|
||||||
|
let styleFeaturedPackages: string;
|
||||||
|
|
||||||
|
function resetFeaturedStyle() {
|
||||||
|
const position = focus * width;
|
||||||
|
styleFeaturedPackages = `
|
||||||
|
width: ${items.length * width}px;
|
||||||
|
left: -${position}px;
|
||||||
|
transition: left 0.6s ease-in;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleContainerResize(node: HTMLElement) {
|
||||||
|
width = node.clientWidth;
|
||||||
|
resetFeaturedStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
let loop: NodeJS.Timer;
|
||||||
|
|
||||||
|
function resetLoop() {
|
||||||
|
if (loop) clearInterval(loop);
|
||||||
|
loop = setInterval(() => {
|
||||||
|
focus++;
|
||||||
|
if (focus === items.length) {
|
||||||
|
focus = 0;
|
||||||
|
}
|
||||||
|
resetFeaturedStyle();
|
||||||
|
}, 3000);
|
||||||
|
resetFeaturedStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(() => clearInterval(loop));
|
||||||
|
onMount(() => {
|
||||||
|
resetLoop();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="h-96 w-full bg-black" use:watchResize={handleContainerResize}>
|
||||||
|
<!-- <Placeholder label="FeaturedPackages" /> -->
|
||||||
|
<header class="flex h-12 items-center justify-between bg-accent px-2">
|
||||||
|
<p>{title}</p>
|
||||||
|
<ul class="flex gap-2">
|
||||||
|
{#each items as _item, i}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
focus = i;
|
||||||
|
resetLoop();
|
||||||
|
}}
|
||||||
|
class={`bg-purple h-4 w-4 rounded-lg border-2 border-white transition-colors ${
|
||||||
|
i === focus ? 'bg-purple-900' : ''
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
<figure class="absolute bottom-0 top-12 left-0 right-0 overflow-hidden">
|
||||||
|
{#if items.length}
|
||||||
|
<section class="absolute top-0 flex h-full" style={styleFeaturedPackages}>
|
||||||
|
{#each items as item}
|
||||||
|
<div class="h-full" style={`width:${width}px`}>
|
||||||
|
<a href={item.link}>
|
||||||
|
<GalleryItem {...item} {width} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</section>
|
||||||
|
{:else}
|
||||||
|
<Preloader />
|
||||||
|
{/if}
|
||||||
|
</figure>
|
||||||
|
</section>
|
|
@ -1,33 +1,35 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '$appcss';
|
import '$appcss';
|
||||||
import type { Package } from '@tea/ui/types';
|
import ImgLoader from '../ImgLoader/ImgLoader.svelte';
|
||||||
import ImgLoader from '@tea/ui/ImgLoader/ImgLoader.svelte';
|
|
||||||
export let pkg: Package;
|
export let width = 0;
|
||||||
export let width: number;
|
export let imageUrl = '';
|
||||||
|
export let title = '';
|
||||||
|
export let subTitle = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<figure class="featured-pkg relative h-full w-full" style={`width:${width}px`}>
|
<figure class="gallery-item relative h-full w-full" style={`width:${width}px`}>
|
||||||
<ImgLoader
|
<ImgLoader
|
||||||
class="featured-img"
|
class="featured-img"
|
||||||
src={!pkg.thumb_image_url.includes('https://tea.xyz')
|
src={!imageUrl.includes('https://tea.xyz')
|
||||||
? 'https://tea.xyz/Images/package-thumb-nolabel4.jpg'
|
? 'https://tea.xyz/Images/package-thumb-nolabel4.jpg'
|
||||||
: pkg.thumb_image_url}
|
: imageUrl}
|
||||||
alt={pkg.name}
|
alt={title}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<article class="card-thumb-label">
|
<article class="card-thumb-label">
|
||||||
<i class="icon-tea-logo-iconasset-1">
|
<i class="icon-tea-logo-iconasset-1">
|
||||||
<!-- TODO: replace with icon.svg -->
|
<!-- TODO: replace with icon.svg -->
|
||||||
</i>
|
</i>
|
||||||
<h3 class="text-3xl">{pkg.name}</h3>
|
<h3 class="text-3xl">{title}</h3>
|
||||||
{#if pkg.maintainer}
|
{#if subTitle}
|
||||||
<h4 class="mt-2 text-lg">• {pkg.maintainer}</h4>
|
<h4 class="mt-2 text-lg">• {subTitle}</h4>
|
||||||
{/if}
|
{/if}
|
||||||
</article>
|
</article>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.featured-pkg :global(.featured-img) {
|
.gallery-item :global(.featured-img) {
|
||||||
box-shadow: 0px 0px 12px #0c0c0c !important;
|
box-shadow: 0px 0px 12px #0c0c0c !important;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
|
@ -98,12 +98,14 @@ importers:
|
||||||
svelte: ^3.44.0
|
svelte: ^3.44.0
|
||||||
svelte-check: ^2.7.1
|
svelte-check: ^2.7.1
|
||||||
svelte-preprocess: ^4.10.7
|
svelte-preprocess: ^4.10.7
|
||||||
|
svelte-watch-resize: ^1.0.3
|
||||||
tailwindcss: ^3.2.4
|
tailwindcss: ^3.2.4
|
||||||
tslib: ^2.3.1
|
tslib: ^2.3.1
|
||||||
typescript: ^4.7.4
|
typescript: ^4.7.4
|
||||||
vite: ^3.1.0
|
vite: ^3.1.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tailwindcss/line-clamp': 0.4.2_tailwindcss@3.2.4
|
'@tailwindcss/line-clamp': 0.4.2_tailwindcss@3.2.4
|
||||||
|
svelte-watch-resize: 1.0.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@playwright/test': 1.25.0
|
'@playwright/test': 1.25.0
|
||||||
'@storybook/addon-essentials': 7.0.0-alpha.51_typescript@4.9.3
|
'@storybook/addon-essentials': 7.0.0-alpha.51_typescript@4.9.3
|
||||||
|
@ -113,7 +115,7 @@ importers:
|
||||||
'@storybook/svelte-vite': 7.0.0-alpha.51_typescript@4.9.3
|
'@storybook/svelte-vite': 7.0.0-alpha.51_typescript@4.9.3
|
||||||
'@storybook/testing-library': 0.0.13
|
'@storybook/testing-library': 0.0.13
|
||||||
'@sveltejs/adapter-auto': 1.0.0-next.90
|
'@sveltejs/adapter-auto': 1.0.0-next.90
|
||||||
'@sveltejs/kit': 1.0.0-next.570_svelte@3.53.1+vite@3.2.4
|
'@sveltejs/kit': 1.0.0-next.572_svelte@3.53.1+vite@3.2.4
|
||||||
'@sveltejs/package': 1.0.0-next.1_7dvewpees4iyn2tkw2qzal77a4
|
'@sveltejs/package': 1.0.0-next.1_7dvewpees4iyn2tkw2qzal77a4
|
||||||
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
|
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
|
||||||
'@typescript-eslint/parser': 5.43.0_e3uo4sehh4zr4i6m57mkkxxv7y
|
'@typescript-eslint/parser': 5.43.0_e3uo4sehh4zr4i6m57mkkxxv7y
|
||||||
|
@ -3242,6 +3244,34 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@sveltejs/kit/1.0.0-next.572_svelte@3.53.1+vite@3.2.4:
|
||||||
|
resolution: {integrity: sha512-PiKEr55L/uJyMKvDPdyoa5MlAYQwdgs8HLMbr28YcCBmhw/v6V7gutKOKdqeXc3YwKEFVS3z7TvW6c7eDokJdQ==}
|
||||||
|
engines: {node: '>=16.14'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^3.44.0
|
||||||
|
vite: ^3.2.0
|
||||||
|
dependencies:
|
||||||
|
'@sveltejs/vite-plugin-svelte': 1.3.1_svelte@3.53.1+vite@3.2.4
|
||||||
|
'@types/cookie': 0.5.1
|
||||||
|
cookie: 0.5.0
|
||||||
|
devalue: 4.2.0
|
||||||
|
kleur: 4.1.5
|
||||||
|
magic-string: 0.27.0
|
||||||
|
mime: 3.0.0
|
||||||
|
sade: 1.8.1
|
||||||
|
set-cookie-parser: 2.5.1
|
||||||
|
sirv: 2.0.2
|
||||||
|
svelte: 3.53.1
|
||||||
|
tiny-glob: 0.2.9
|
||||||
|
undici: 5.13.0
|
||||||
|
vite: 3.2.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- diff-match-patch
|
||||||
|
- supports-color
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@sveltejs/package/1.0.0-next.1_7dvewpees4iyn2tkw2qzal77a4:
|
/@sveltejs/package/1.0.0-next.1_7dvewpees4iyn2tkw2qzal77a4:
|
||||||
resolution: {integrity: sha512-U8XBk6Cfy8MjKG41Uyo+fqBpdhu7xUSnhiCNoODRaAtWV02RZoLh+McXrsxEvqi/ycgymctlhJhssqDnD+E+FA==}
|
resolution: {integrity: sha512-U8XBk6Cfy8MjKG41Uyo+fqBpdhu7xUSnhiCNoODRaAtWV02RZoLh+McXrsxEvqi/ycgymctlhJhssqDnD+E+FA==}
|
||||||
engines: {node: '>=16.9'}
|
engines: {node: '>=16.9'}
|
||||||
|
@ -7382,6 +7412,13 @@ packages:
|
||||||
sourcemap-codec: 1.4.8
|
sourcemap-codec: 1.4.8
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/magic-string/0.27.0:
|
||||||
|
resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
dev: true
|
||||||
|
|
||||||
/make-dir/2.1.0:
|
/make-dir/2.1.0:
|
||||||
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
Loading…
Reference in a new issue