mirror of
https://github.com/ivabus/gui
synced 2025-04-23 14:07:14 +03:00
parent
8766702c35
commit
d6dbffa6e5
12 changed files with 184 additions and 35 deletions
|
@ -6,11 +6,13 @@
|
|||
import type { GUIPackage } from '$libs/types';
|
||||
import { PackageStates } from '$libs/types';
|
||||
import { getPackage } from '@native';
|
||||
import { notificationStore } from '$libs/stores';
|
||||
import { packagesStore, notificationStore } from '$libs/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import semverCompare from "semver/functions/compare";
|
||||
|
||||
export let tab = "all";
|
||||
export let pkg: GUIPackage;
|
||||
export let onClick: () => void;
|
||||
export let onClick: (version: string) => void;
|
||||
|
||||
let fakeLoadingProgress = 0;
|
||||
|
||||
|
@ -41,31 +43,50 @@
|
|||
fakeLoadingProgress = fakeLoadingProgress + addProgress;
|
||||
}, ms);
|
||||
}
|
||||
|
||||
const onClickCTA = async (version: string) => {
|
||||
fakeLoadingProgress = 1;
|
||||
startFakeLoader();
|
||||
await onClick(version);
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
notificationStore.add({
|
||||
message: `Package ${pkg.full_name} v${pkg.version} has been installed.`,
|
||||
});
|
||||
clearTimeout(fakeTimer);
|
||||
fakeLoadingProgress = 100;
|
||||
resolve(null);
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
const findAvailableVersions = (pkg: GUIPackage) => {
|
||||
// default to just showing the latest if bottles haven't loaded yet
|
||||
if (!pkg.bottles) {
|
||||
return [pkg.version]
|
||||
}
|
||||
|
||||
const versionSet = new Set<string>()
|
||||
for (const b of pkg.bottles) {
|
||||
versionSet.add(b.version)
|
||||
}
|
||||
|
||||
return Array.from(versionSet).sort((a, b) => semverCompare(b, a));
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
packagesStore.fetchPackageBottles(pkg.full_name)
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<PackageCard
|
||||
{pkg}
|
||||
availableVersions={findAvailableVersions(pkg)}
|
||||
link={`/packages/${pkg.slug}?tab=${tab}`}
|
||||
ctaLabel={getCTALabel(pkg.state)}
|
||||
progessLoading={+fakeLoadingProgress.toFixed(2)}
|
||||
ctaType="plain"
|
||||
ctaColor={PackageStates.INSTALLED === pkg.state ? "green" : "secondary"}
|
||||
onClickCTA={((pkg) => {
|
||||
return async () => {
|
||||
fakeLoadingProgress = 1;
|
||||
startFakeLoader();
|
||||
await onClick();
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
notificationStore.add({
|
||||
message: `Package ${pkg.full_name} v${pkg.version} has been installed.`,
|
||||
});
|
||||
clearTimeout(fakeTimer);
|
||||
fakeLoadingProgress = 100;
|
||||
resolve(null);
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
})(pkg)}
|
||||
/>
|
||||
{onClickCTA}
|
||||
/>
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<Package
|
||||
tab={packageFilter}
|
||||
{pkg}
|
||||
onClick={() => packagesStore.installPkg(pkg)}
|
||||
onClick={(version) => packagesStore.installPkg(pkg, version)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -94,4 +94,4 @@
|
|||
box-shadow: inset 0vw 0vw 0vw 0.223vw #1a1a1a !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -91,6 +91,7 @@ export async function installPackage(pkg: GUIPackage, version?: string) {
|
|||
try {
|
||||
const latestVersion = pkg.version;
|
||||
const specificVersion = version || latestVersion;
|
||||
log.info(`installing package: ${pkg.name} version: ${specificVersion}`);
|
||||
await installPackageCommand(pkg.full_name + (specificVersion ? `@${specificVersion}` : ""));
|
||||
} catch (error) {
|
||||
log.error("installPackage:", error);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { writable } from "svelte/store";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import { l } from "$libs/translations";
|
||||
import { NotificationType } from "@tea/ui/types";
|
||||
import type { Notification } from "@tea/ui/types";
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ import {
|
|||
getDistPackages,
|
||||
openTerminal,
|
||||
getInstalledPackages,
|
||||
installPackage
|
||||
installPackage,
|
||||
getPackageBottles
|
||||
} from "@native";
|
||||
|
||||
import { getReadme, getContributors, getRepoAsPackage } from "$libs/github";
|
||||
|
@ -157,6 +158,12 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
|
|||
}
|
||||
};
|
||||
|
||||
const fetchPackageBottles = async (pkgName: string) => {
|
||||
// TODO: this api should take an architecture argument or else an architecture filter should be applied downstreawm
|
||||
const bottles = await getPackageBottles(pkgName);
|
||||
updatePackage(pkgName, { bottles });
|
||||
};
|
||||
|
||||
return {
|
||||
packages,
|
||||
syncProgress,
|
||||
|
@ -178,6 +185,7 @@ To read more about this package go to [${guiPkg.homepage}](${guiPkg.homepage}).
|
|||
}
|
||||
});
|
||||
},
|
||||
fetchPackageBottles,
|
||||
updatePackage,
|
||||
init,
|
||||
installPkg
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// please use the package @tea/ui/src/types.ts
|
||||
// things that go there are shared types/shapes like ie: Package
|
||||
|
||||
import type { Package, Developer, Bottle } from "@tea/ui/types";
|
||||
import type { Package, Developer } from "@tea/ui/types";
|
||||
|
||||
export enum PackageStates {
|
||||
AVAILABLE = "AVAILABLE",
|
||||
|
|
|
@ -29,5 +29,13 @@ module.exports = {
|
|||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
argsIgnorePattern: "^_"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
8
modules/ui/src/app.d.ts
vendored
8
modules/ui/src/app.d.ts
vendored
|
@ -9,3 +9,11 @@ declare namespace App {
|
|||
// interface Error {}
|
||||
// interface Platform {}
|
||||
}
|
||||
|
||||
// Declare custom event handlers here to make typscript happy.
|
||||
declare namespace svelte.JSX {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface HTMLAttributes<T> {
|
||||
onclick_outside: () => void;
|
||||
}
|
||||
}
|
||||
|
|
20
modules/ui/src/lib/clickOutside.ts
Normal file
20
modules/ui/src/lib/clickOutside.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Use this script on an element with use:clickOutside to register for click events that occur
|
||||
* outside of the element
|
||||
*/
|
||||
export default function clickOutside(element: HTMLElement) {
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
const event = e as MouseEvent & { target?: Node };
|
||||
if (element && !element.contains(event.target) && !event.defaultPrevented) {
|
||||
element.dispatchEvent(new CustomEvent("click_outside"));
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", handleClick, true);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
document.removeEventListener("click", handleClick, true);
|
||||
}
|
||||
};
|
||||
}
|
89
modules/ui/src/package-card/install-button.svelte
Normal file
89
modules/ui/src/package-card/install-button.svelte
Normal file
|
@ -0,0 +1,89 @@
|
|||
<script lang="ts">
|
||||
import clickOutside from "../lib/clickOutside";
|
||||
import Button from "../button/button.svelte";
|
||||
|
||||
export let ctaLabel: string;
|
||||
export let ctaType: "ghost" | "plain" = "ghost";
|
||||
export let ctaColor: "green" | "secondary" = "secondary";
|
||||
|
||||
export let onClickCTA = async (_version: string) => {
|
||||
console.log("do nothing");
|
||||
};
|
||||
|
||||
export let availableVersions: string[] = [];
|
||||
|
||||
$: isOpened = false;
|
||||
|
||||
const handleClick = (version: string) => {
|
||||
isOpened = false;
|
||||
onClickCTA(version);
|
||||
};
|
||||
|
||||
const handleClickOutside = () => (isOpened = false);
|
||||
</script>
|
||||
|
||||
<div class="dropdown" use:clickOutside on:click_outside={handleClickOutside}>
|
||||
<Button
|
||||
class="h-8 border border-gray p-2 text-xs"
|
||||
type={ctaType}
|
||||
color={ctaColor}
|
||||
onClick={() => (isOpened = !isOpened)}
|
||||
>
|
||||
{ctaLabel}
|
||||
</Button>
|
||||
<div class="version-list" class:visible={isOpened}>
|
||||
{#each availableVersions as version, idx}
|
||||
{#if idx !== 0}<hr class="divider" />{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="version-item text-xs" on:click={() => handleClick(version)}>
|
||||
v{version + (idx === 0 ? " (latest)" : "")}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.version-list {
|
||||
display: none;
|
||||
position: absolute;
|
||||
margin-top: 4px;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
background-color: #1a1a1a;
|
||||
border: 0.5px solid #949494;
|
||||
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 2px;
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.version-item {
|
||||
cursor: pointer;
|
||||
margin: 4px 6px;
|
||||
padding: 4px 6px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version-item:hover {
|
||||
border: 1px solid #949494;
|
||||
background-color: rgba(148, 148, 148, 0.35);
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border: 1px solid #272626;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.visible {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
|
@ -2,17 +2,18 @@
|
|||
import "../app.css";
|
||||
import type { Package } from "../types";
|
||||
import ImgLoader from "../img-loader/img-loader.svelte";
|
||||
import Button from "../button/button.svelte";
|
||||
import ProgressCircle from "../progress-circle/progress-circle.svelte";
|
||||
import InstallButton from "./install-button.svelte";
|
||||
|
||||
export let pkg: Package;
|
||||
export let availableVersions: string[];
|
||||
export let link: string;
|
||||
export let ctaLabel: string;
|
||||
export let progessLoading = 0;
|
||||
export let ctaType: "ghost" | "plain" = "ghost";
|
||||
export let ctaColor: "green" | "secondary" = "secondary";
|
||||
|
||||
export let onClickCTA = () => {
|
||||
export let onClickCTA = async (_version: string) => {
|
||||
console.log("do nothing");
|
||||
};
|
||||
</script>
|
||||
|
@ -48,14 +49,7 @@
|
|||
<span class="package-install-no">>{{- .installs -}} installs</span> -->
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
class="h-8 w-1/2 border border-gray p-2 text-xs"
|
||||
onClick={onClickCTA}
|
||||
type={ctaType}
|
||||
color={ctaColor}
|
||||
>
|
||||
{ctaLabel}
|
||||
</Button>
|
||||
<InstallButton {ctaLabel} {ctaColor} {ctaType} {onClickCTA} {availableVersions} />
|
||||
</footer>
|
||||
{#if progessLoading > 0 && progessLoading < 100}
|
||||
<div class="absolute top-0 left-0 h-full w-full bg-black bg-opacity-50">
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
},
|
||||
"include": [
|
||||
"ambient.d.ts",
|
||||
"./src/app.d.ts",
|
||||
"./types/**/$types.d.ts",
|
||||
"../vite.config.ts",
|
||||
"../src/**/*.js",
|
||||
|
|
Loading…
Reference in a new issue