mirror of
https://github.com/ivabus/gui
synced 2025-06-06 23:30:26 +03:00
parent
b3731a136d
commit
9970b686bb
9 changed files with 217 additions and 91 deletions
|
@ -1,7 +1,26 @@
|
|||
<script lang="ts">
|
||||
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>
|
||||
|
||||
<Placeholder label="FeaturedCourses" />
|
||||
<h1>test</h1>
|
||||
<Gallery
|
||||
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">
|
||||
import '$appcss';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { watchResize } from 'svelte-watch-resize';
|
||||
import { onMount } from 'svelte';
|
||||
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 {
|
||||
featuredPackages as featuredPackagesStore,
|
||||
initializeFeaturedPackages
|
||||
|
@ -12,82 +11,23 @@
|
|||
|
||||
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) => {
|
||||
featuredPackages = v;
|
||||
});
|
||||
|
||||
onDestroy(() => clearInterval(loop));
|
||||
onMount(() => {
|
||||
if (!featuredPackages.length) {
|
||||
initializeFeaturedPackages();
|
||||
}
|
||||
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>FEATURED PACKAGES</p>
|
||||
<ul class="flex gap-2">
|
||||
{#each featuredPackages as pkg, i}
|
||||
<button
|
||||
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>
|
||||
<Gallery
|
||||
title="FEATURED PACKAGES"
|
||||
items={featuredPackages.map((pkg) => ({
|
||||
title: pkg.full_name,
|
||||
subTitle: pkg.maintainer || '',
|
||||
imageUrl: pkg.thumb_image_url,
|
||||
link: `/packages/${pkg.slug}`
|
||||
}))}
|
||||
/>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* * make cors work with api.tea.xyz/v1
|
||||
*/
|
||||
import type { Package, Review } from '@tea/ui/types';
|
||||
import type { GUIPackage } from '../types';
|
||||
import type { GUIPackage, Course } from '../types';
|
||||
import { PackageStates } from '../types';
|
||||
import { loremIpsum } from 'lorem-ipsum';
|
||||
import _ from 'lodash';
|
||||
|
@ -216,3 +216,28 @@ export async function installPackage(full_name: string) {
|
|||
function delay(ms: number) {
|
||||
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 { Buffer } from 'buffer';
|
||||
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 { PackageStates } from '../types';
|
||||
|
||||
|
@ -137,3 +137,8 @@ async function getInstalledPackages() {
|
|||
});
|
||||
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;
|
||||
installed_version?: string;
|
||||
};
|
||||
|
||||
export type Course = {
|
||||
title: string;
|
||||
sub_title: string;
|
||||
banner_image_url: string;
|
||||
link: string;
|
||||
};
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
},
|
||||
"type": "module",
|
||||
"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">
|
||||
import '$appcss';
|
||||
import type { Package } from '@tea/ui/types';
|
||||
import ImgLoader from '@tea/ui/ImgLoader/ImgLoader.svelte';
|
||||
export let pkg: Package;
|
||||
export let width: number;
|
||||
import ImgLoader from '../ImgLoader/ImgLoader.svelte';
|
||||
|
||||
export let width = 0;
|
||||
export let imageUrl = '';
|
||||
export let title = '';
|
||||
export let subTitle = '';
|
||||
</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
|
||||
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'
|
||||
: pkg.thumb_image_url}
|
||||
alt={pkg.name}
|
||||
: imageUrl}
|
||||
alt={title}
|
||||
/>
|
||||
|
||||
<article class="card-thumb-label">
|
||||
<i class="icon-tea-logo-iconasset-1">
|
||||
<!-- TODO: replace with icon.svg -->
|
||||
</i>
|
||||
<h3 class="text-3xl">{pkg.name}</h3>
|
||||
{#if pkg.maintainer}
|
||||
<h4 class="mt-2 text-lg">• {pkg.maintainer}</h4>
|
||||
<h3 class="text-3xl">{title}</h3>
|
||||
{#if subTitle}
|
||||
<h4 class="mt-2 text-lg">• {subTitle}</h4>
|
||||
{/if}
|
||||
</article>
|
||||
</figure>
|
||||
|
||||
<style>
|
||||
.featured-pkg :global(.featured-img) {
|
||||
.gallery-item :global(.featured-img) {
|
||||
box-shadow: 0px 0px 12px #0c0c0c !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
|
@ -98,12 +98,14 @@ importers:
|
|||
svelte: ^3.44.0
|
||||
svelte-check: ^2.7.1
|
||||
svelte-preprocess: ^4.10.7
|
||||
svelte-watch-resize: ^1.0.3
|
||||
tailwindcss: ^3.2.4
|
||||
tslib: ^2.3.1
|
||||
typescript: ^4.7.4
|
||||
vite: ^3.1.0
|
||||
dependencies:
|
||||
'@tailwindcss/line-clamp': 0.4.2_tailwindcss@3.2.4
|
||||
svelte-watch-resize: 1.0.3
|
||||
devDependencies:
|
||||
'@playwright/test': 1.25.0
|
||||
'@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/testing-library': 0.0.13
|
||||
'@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
|
||||
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
|
||||
'@typescript-eslint/parser': 5.43.0_e3uo4sehh4zr4i6m57mkkxxv7y
|
||||
|
@ -3242,6 +3244,34 @@ packages:
|
|||
- supports-color
|
||||
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:
|
||||
resolution: {integrity: sha512-U8XBk6Cfy8MjKG41Uyo+fqBpdhu7xUSnhiCNoODRaAtWV02RZoLh+McXrsxEvqi/ycgymctlhJhssqDnD+E+FA==}
|
||||
engines: {node: '>=16.9'}
|
||||
|
@ -7382,6 +7412,13 @@ packages:
|
|||
sourcemap-codec: 1.4.8
|
||||
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:
|
||||
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
Loading…
Reference in a new issue