* build(pages): move html pages generator to vite plugin

* refactor: remove all router related content

* refactor: replace tailwindcss by unocss

* refactor(app): upgrade app to Svelte 5

* build(pages): restore html pages generator on vite config

* chore: run prettier

* fix(ui): fix notification center slot render

* chore: upgrade dependencies
This commit is contained in:
Mathieu Schimmerling
2024-10-06 20:13:08 +02:00
committed by GitHub
parent f223b2f338
commit 56c957a173
60 changed files with 1457 additions and 1386 deletions

View File

@@ -1,11 +1,9 @@
{
"recommendations": [
"astro-build.astro-vscode",
"bradlc.vscode-tailwindcss",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"svelte.svelte-vscode",
"vue.volar"
"antfu.unocss"
],
"unwantedRecommendations": []
}

View File

@@ -1,47 +0,0 @@
<footer class="bg-gray-900">
<h2 class="sr-only">Footer</h2>
<div class="mx-auto max-w-7xl px-6 pb-20 pt-16 sm:pt-24 lg:px-8">
<div class="xl:grid xl:grid-cols-3 xl:gap-8">
<div class="space-y-8">
<a class="font-semibold text-xl flex items-center space-x-3" href="/">
<img src="/popper.svg" alt="logo" class="size-7" />
<span>Component party</span>
</a>
<p class="text-sm leading-6 text-gray-300">
Web component JavaScript frameworks overview by their syntax and
features
</p>
<div class="flex space-x-6">
<a href="https://github.com/matschik/component-party.dev" class="text-gray-500 hover:text-gray-400">
<span class="sr-only">GitHub</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" />
</svg>
</a>
</div>
</div>
<div class="mt-16 grid grid-cols-1 sm:grid-cols-2 gap-8 xl:col-span-2 xl:mt-0">
<% for (const navigation of it.navigations) { %>
<div class="md:grid md:gap-8">
<aside>
<h3 class="text-sm font-semibold leading-6 text-white">
<%= navigation.title %>
</h3>
<nav>
<ul role="list" class="mt-2 space-y-2">
<% for (const link of navigation.links) { %>
<li>
<a href="<%= link.url %>" class="text-sm leading-6 text-gray-300 hover:text-white">
<%= link.name %>
</a>
</li>
<% } %>
</ul>
</nav>
</aside>
</div>
<% } %>
</div>
</div>
</div>
</footer>

View File

@@ -0,0 +1,64 @@
<footer class="bg-gray-900">
<h2 class="sr-only">Footer</h2>
<div class="mx-auto max-w-7xl px-6 pb-20 pt-16 sm:pt-24 lg:px-8">
<div class="xl:grid xl:grid-cols-3 xl:gap-8">
<div class="space-y-8">
<a class="font-semibold text-xl flex items-center space-x-3" href="/">
<img src="/popper.svg" alt="logo" class="size-7" />
<span>Component party</span>
</a>
<p class="text-sm leading-6 text-gray-300">
Web component JavaScript frameworks overview by their syntax and
features
</p>
<div class="flex space-x-6">
<a
href="https://github.com/matschik/component-party.dev"
class="text-gray-500 hover:text-gray-400"
>
<span class="sr-only">GitHub</span>
<svg
class="h-6 w-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clip-rule="evenodd"
/>
</svg>
</a>
</div>
</div>
<div
class="mt-16 grid grid-cols-1 sm:grid-cols-2 gap-8 xl:col-span-2 xl:mt-0"
>
<% for (const navigation of it.navigations) { %>
<div class="md:grid md:gap-8">
<aside>
<h3 class="text-sm font-semibold leading-6 text-white">
<%= navigation.title %>
</h3>
<nav>
<ul role="list" class="mt-2 space-y-2">
<% for (const link of navigation.links) { %>
<li>
<a
href="<%= link.url %>"
class="text-sm leading-6 text-gray-300 hover:text-white"
>
<%= link.name %>
</a>
</li>
<% } %>
</ul>
</nav>
</aside>
</div>
<% } %>
</div>
</div>
</div>
</footer>

View File

@@ -2,7 +2,7 @@ import { component$, useVisibleTask$, useSignal } from "@builder.io/qwik";
export const InputFocused = component$(() => {
const inputElement = useSignal<HTMLInputElement>();
useVisibleTask$(({ track }) => {
const el = track(inputElement);
el?.focus();

View File

@@ -1,8 +0,0 @@
Using [location.hash](https://developer.mozilla.org/en-US/docs/Web/API/Location/hash)
```html
<nav>
<a href="#">Index</a>
<a href="#contact">Contact Us</a>
</nav>
```

View File

@@ -1,8 +0,0 @@
<ul>
<li>
<a routerLink="/home">Home</a>
</li>
<li>
<a routerLink="/about">About</a>
</li>
</ul>

View File

@@ -1,16 +0,0 @@
Using `RouterConfiguration` with `options.pushState = false`
See [Building the Application Shell](http://aurelia.io/docs/tutorials/creating-a-contact-manager#building-the-application-shell)
```html
<template>
<ul>
<li>
<a routerLink="/#/home">Home</a>
</li>
<li>
<a routerLink="/#/about">About</a>
</li>
</ul>
</template>
```

View File

@@ -1,12 +0,0 @@
See [Add routes and a viewport](https://docs.aurelia.io/routing/router-tutorial#add-routes-and-a-viewport)
```html
<ul>
<li>
<a load="/home">Home</a>
</li>
<li>
<a load="/about">About</a>
</li>
</ul>
```

View File

@@ -1,16 +0,0 @@
Built-in <a href="https://guides.emberjs.com/release/routing/linking-between-routes/">routing</a>
```hbs
<ul>
<li>
<LinkTo @route="index">
Home
</LinkTo>
</li>
<li>
<LinkTo @route="about">
About us
</LinkTo>
</li>
</ul>
```

View File

@@ -1,12 +0,0 @@
With [@vaadin/router](https://vaadin.com/router)
```html
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/about">About us</a>
</li>
</ul>
```

View File

@@ -1,8 +0,0 @@
With [`@marko/cli`s `build` or `serve`](https://github.com/marko-js/cli/tree/main/packages/serve)
```marko
<nav>
<a href="/">Index</a>
<a href="/contact">Contact Us</a>
</nav>
```

View File

@@ -1,8 +0,0 @@
With [`@marko/run`](https://github.com/marko-js/run/tree/main/packages/serve)
```marko
<nav>
<a href="/">Index</a>
<a href="/contact">Contact Us</a>
</nav>
```

View File

@@ -1,14 +0,0 @@
import m from "mithril";
export default function App() {
const navigateTo = (loc) => m.route.set(loc);
return {
view: () =>
m(
"ul",
m("li", m("button", { onclick: navigateTo("/foo") }, "foo")),
m("li", m("button", { onclick: navigateTo("/bar") }, "bar"))
),
};
}

View File

@@ -1,21 +0,0 @@
With [qwik-city](https://qwik.builder.io/qwikcity/overview/)
```jsx
import { component$ } from "@builder.io/qwik";
import { Link } from "@builder.io/qwik-city";
const Home = component$(() => {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About us</Link>
</li>
</ul>
);
});
export default Home;
```

View File

@@ -1,18 +0,0 @@
With <a href="https://nextjs.org/docs/api-reference/next/link">NextJS</a>
```jsx
import Link from "next/link";
export default function Home() {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About us</Link>
</li>
</ul>
);
}
```

View File

@@ -1,18 +0,0 @@
With [solid-app-router](https://github.com/solidjs/solid-app-router)
```jsx
import { Link } from "solid-app-router";
export default function Home() {
return (
<ul>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About us</Link>
</li>
</ul>
);
}
```

View File

@@ -1,12 +0,0 @@
With [SvelteKit](https://kit.svelte.dev/docs/routing#pages)
```svelte
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/about">About us</a>
</li>
</ul>
```

View File

@@ -1,12 +0,0 @@
With [SvelteKit](https://kit.svelte.dev/docs/routing#pages)
```svelte
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/about">About us</a>
</li>
</ul>
```

View File

@@ -1,12 +0,0 @@
```vue
<template>
<ul>
<li>
<RouterLink to="/"> Home </RouterLink>
</li>
<li>
<RouterLink to="/about"> About us </RouterLink>
</li>
</ul>
</template>
```

View File

@@ -1,14 +0,0 @@
With <a href="https://v3.nuxtjs.org/guide/directory-structure/pages#navigation">Nuxt 3</a>
```vue
<template>
<ul>
<li>
<NuxtLink to="/"> Home </NuxtLink>
</li>
<li>
<NuxtLink to="/about"> About us </NuxtLink>
</li>
</ul>
</template>
```

View File

@@ -1,21 +0,0 @@
Using [location.hash](https://developer.mozilla.org/en-US/docs/Web/API/Location/hash)
```html
<nav>
<a href="#">Index</a>
<a href="#contact">Contact Us</a>
</nav>
<div x-data="{page: location.hash}" @hashchange.window="page = location.hash">
<span x-show="page === ''">
<h1>Welcome</h1>
</span>
<span x-show="page === '#contact'">
<p>
<ul>
<li>Lorem ipsum dolor</li>
<li>amet consectetur adipisicing elit</li>
</ul>
</p>
</span>
</div>
```

View File

@@ -1,28 +0,0 @@
import { RouterModule } from "@angular/router";
import { NgModule, Component } from "@angular/core";
import { HomeComponent } from "./home.component";
import { AboutComponent } from "./about.component";
@Component({
selector: "app-root",
template: `
<ul>
<li><a routerLink="/home">Home</a></li>
<li><a routerLink="/about">About</a></li>
</ul>
<router-outlet></router-outlet>
`,
})
export class AppComponent {}
@NgModule({
imports: [
RouterModule.forRoot([
{ path: "home", component: HomeComponent },
{ path: "about", component: AboutComponent },
]),
],
declarations: [AppComponent, HomeComponent, AboutComponent],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@@ -1,8 +0,0 @@
<template>
<ul repeat.for="nav of router.navigation">
<li class="${nav.isActive ? 'active' : ''}">
<a href.bind="nav.href">${nav.title}</a>
</li>
</ul>
<router-view></router-view>
</template>

View File

@@ -1,38 +0,0 @@
import { PLATFORM } from "aurelia-pal";
import { AppRouter } from "aurelia-router";
export class App {
router: AppRouter = null;
configureRouter(config, router) {
this.router = router;
config.title = "Aurelia";
config.map([
{
route: ["", "home"],
name: "home",
moduleId: PLATFORM.moduleName("home/index"),
},
{
route: "users",
name: "users",
moduleId: PLATFORM.moduleName("users/index"),
nav: true,
title: "Users",
},
{
route: "users/:id/detail",
name: "userDetail",
moduleId: PLATFORM.moduleName("users/detail"),
},
{
route: "files/*path",
name: "files",
moduleId: PLATFORM.moduleName("files/index"),
nav: 0,
title: "Files",
href: "#files",
},
]);
}
}

View File

@@ -1,8 +0,0 @@
export class App {
static routes = [
{
path: ["", "home"],
},
];
static template = "<au-viewport></au-viewport>";
}

View File

@@ -1,23 +0,0 @@
Built-in <a href="https://guides.emberjs.com/release/routing/">routing</a>
Given an `app/router.js` config of:
```js
Router.map(function () {
this.route("about");
this.route("fallback", { path: "*" });
});
```
```
|-- routes/
|-- application.js // global app setup
|-- index.js // handles minimally required data to show "/"
|-- about.js // handles minimally required data to show "/about"
|-- fallback.js // fallback page
|-- templates/
|-- application.hbs // global app layout
|-- index.hbs // index page "/"
|-- about.hbs // about page "/about"
|-- fallback.hbs // fallback layout
```

View File

@@ -1,18 +0,0 @@
With [@vaadin/router](https://vaadin.com/router)
```html
<div id="outlet"></div>
<script>
// import {Router} from '@vaadin/router'; // for Webpack / Polymer CLI
// const Router = Vaadin.Router; // for vaadin-router.umd.js
// select the DOM node where the router inserts route Web Components
const outlet = document.getElementById("outlet");
const router = new Router(outlet);
router.setRoutes([
{ path: "/", component: "x-home-view" },
{ path: "/about", component: "x-about-view" },
]);
</script>
```

View File

@@ -1,9 +0,0 @@
With [`@marko/cli`s `build` or `serve`](https://github.com/marko-js/cli/tree/main/packages/serve)
```
index.marko // index page "/"
about.marko // about page "/about"
hello/
|-- [name].marko // dynamic Hello page "/hello/Emily"
[rest].marko // dynamic parameter can be used as catch-all to show 404 page
```

View File

@@ -1,19 +0,0 @@
With [`@marko/run`](https://github.com/marko-js/run/tree/main/packages/serve)
```
routes/
|-- +page.marko // index page "/"
|-- about/
|-- +page.marko // about page "/about"
|-- +layout.marko // global app layout
|-- +handler.{js,ts,*} // conditionally render HTML, API route, run arbitrary code…
|-- +middleware.{js,ts,*} // added to HTTP framework middleware chain
|-- +meta.{js,ts,*} // adds metadata to route
|-- +404.marko // shows when no suitable route found
|-- +500.marko // shows when any route throws
|-- /path/_less/
|-- +page.marko // pathless directory, page "/path"
|-- /$dynamic/
|-- +page.marko // dynamic parameter, can be used as a route-specific 404
|-- /$$catchall/ // like dynamic, but consumes all path segments until the end
```

View File

@@ -1,12 +0,0 @@
import m from "mithril";
export default function App() {
return {
view: () =>
m(
"ul",
m("li", m(m.route.Link, { href: "/foo" }, "foo")),
m("li", m(m.route.Link, { href: "/bar" }, "bar"))
),
};
}

View File

@@ -1,11 +0,0 @@
With [qwik-city](https://qwik.builder.io/qwikcity/overview/)
```
|-- routes/
|-- index.tsx // index page "/"
|-- about/
|-- index.tsx // about page "/about"
|-- layout.tsx // global app layout
|-- [rest]/
|-- index.tsx // dynamic parameter, can be used as catch-all to show 404 page
```

View File

@@ -1,10 +0,0 @@
With <a href="https://nextjs.org/docs/basic-features/pages">NextJS</a>
```
|-- pages/
|-- index.js // index page "/"
|-- about.js // about page "/about"
|-- 404.js // handle error HTTP 404 page not found
|-- 500.js // handle error HTTP 500
|-- _app.js // global app layout
```

View File

@@ -1,9 +0,0 @@
With <a href="https://remix.run/docs/en/v1/guides/routing">Remix</a>
```
|-- root.jsx // global app layout
|-- routes/
|-- index.jsx // index page "/"
|-- about.jsx // about page "/about"
|-- $.jsx // fallback page
```

View File

@@ -1,26 +0,0 @@
With [solid-app-router](https://github.com/solidjs/solid-app-router)
```jsx
import { render } from "solid-js/web";
import { Router, Routes, Route } from "solid-app-router";
import About from "./About";
import Home from "./Home";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
);
}
render(
() => (
<Router>
<App />
</Router>
),
document.getElementById("root")
);
```

View File

@@ -1,10 +0,0 @@
With [SvelteKit](https://kit.svelte.dev/docs/routing#pages)
```
|-- routes/
|-- +page.svelte // index page "/"
|-- about/
|-- +page.svelte // about page "/about"
|-- +error.svelte // handle HTTP errors 404, 500,...
|-- +layout.svelte // global app layout
```

View File

@@ -1,10 +0,0 @@
With [SvelteKit](https://kit.svelte.dev/docs/routing#pages)
```
|-- routes/
|-- +page.svelte // index page "/"
|-- about/
|-- +page.svelte // about page "/about"
|-- +error.svelte // handle HTTP errors 404, 500,...
|-- +layout.svelte // global app layout
```

View File

@@ -1,5 +0,0 @@
```
|-- pages/
|-- index.vue // index page "/"
|-- about.vue // about page "/about"
```

View File

@@ -1,7 +0,0 @@
With <a href="https://v3.nuxtjs.org/guide/directory-structure/pages">Nuxt 3</a>
```
|-- pages/
|-- index.vue // index page "/"
|-- about.vue // about page "/about"
```

View File

@@ -325,7 +325,12 @@ const frameworks = [
"https://stackblitz.com/edit/au2-conventions?file=src%2Fmy-app.html",
documentationURL: "http://docs.aurelia.io",
filesSorter(files) {
return sortAllFilenames(files, ["index.html", "main.ts", "app.html", "app.ts"]);
return sortAllFilenames(files, [
"index.html",
"main.ts",
"app.html",
"app.ts",
]);
},
repositoryLink: "https://github.com/aurelia/aurelia",
mainPackageName: "aurelia",
@@ -403,7 +408,12 @@ const frameworks = [
playgroundURL: "https://codesandbox.io/s/ppmy26opw7",
documentationURL: "http://aurelia.io/docs/",
filesSorter(files) {
return sortAllFilenames(files, ["index.html", "main.ts", "app.html", "app.ts"]);
return sortAllFilenames(files, [
"index.html",
"main.ts",
"app.html",
"app.ts",
]);
},
repositoryLink: "https://github.com/aurelia/framework",
mainPackageName: "aurelia-framework",

View File

@@ -3,7 +3,7 @@
"private": true,
"version": "2.0.0",
"type": "module",
"packageManager": "pnpm@9.5.0",
"packageManager": "pnpm@9.12.0",
"repository": "github:matschik/component-party.dev",
"scripts": {
"dev": "vite",
@@ -22,9 +22,8 @@
"cy:e2e": "start-server-and-test dev http-get://localhost:5173 cy:open-e2e"
},
"dependencies": {
"@veljs/svelte": "^0.1.11",
"@iconify-json/heroicons": "^1.2.0",
"classnames": "^2.5.1",
"heroiconsvelte": "^1.0.2",
"radix3": "^1.1.2"
},
"devDependencies": {
@@ -39,11 +38,15 @@
"@builder.io/qwik": "^1.9.0",
"@lit/context": "^1.1.2",
"@matschik/lz-string": "^0.0.2",
"@shikijs/markdown-it": "^1.21.0",
"@sveltejs/vite-plugin-svelte": "^3.1.2",
"@tailwindcss/typography": "^0.5.15",
"@shikijs/markdown-it": "^1.21.1",
"@sveltejs/vite-plugin-svelte": "4.0.0-next.7",
"@sveltejs/vite-plugin-svelte-inspector": "3.0.0-next.3",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"@unocss/preset-icons": "^0.63.3",
"@unocss/preset-typography": "^0.63.3",
"@unocss/preset-wind": "^0.63.3",
"@unocss/reset": "^0.63.3",
"aurelia": "2.0.0-beta.22",
"aurelia-framework": "^1.4.1",
"autoprefixer": "^10.4.20",
@@ -71,19 +74,17 @@
"markdown-it": "^14.1.0",
"micache": "^2.4.1",
"pkg-dir": "^8.0.0",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"prettier-plugin-svelte": "^3.2.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"shiki": "^1.21.0",
"shiki": "^1.21.1",
"solid-js": "^1.9.1",
"start-server-and-test": "^2.0.8",
"svelte": "^4.2.19",
"svelte-preprocess": "^6.0.3",
"tailwindcss": "^3.4.13",
"svelte": "5.0.0-next.262",
"tslib": "^2.7.0",
"typescript": "^5.6.2",
"unocss": "^0.63.3",
"vite": "^5.4.8",
"vite-plugin-html": "^3.2.2",
"vue": "^3.5.11"
@@ -93,4 +94,4 @@
"**/svelte4/*.svelte": "eslint --cache --fix",
"*.{js,jsx,ts,tsx,svelte,vue,html,md,css,hbs}": "prettier --cache --write"
}
}
}

1511
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -1,10 +1,11 @@
<script>
import FRAMEWORKS, { matchFrameworkId } from "../frameworks.mjs";
import { SvelteMap, SvelteSet } from "svelte/reactivity";
import c from "classnames";
import FRAMEWORKS, { matchFrameworkId } from "../frameworks.mjs";
import FrameworkLabel from "./components/FrameworkLabel.svelte";
import { sections, snippets } from "./generatedContent/tree.js";
import snippetsImporterByFrameworkId from "./generatedContent/framework/index.js";
import { PlayIcon, PencilIcon, ArrowUpIcon } from "heroiconsvelte/24/outline";
import CodeEditor from "./components/CodeEditor.svelte";
import AppNotificationCenter from "./components/AppNotificationCenter.svelte";
import createLocaleStorage from "./lib/createLocaleStorage.js";
@@ -40,12 +41,13 @@
}
const frameworkIdsFromURLKey = "f";
let frameworkIdsSelected = new Set();
let snippetsByFrameworkId = new Map();
let frameworkIdsSelectedInitialized = false;
let isVersusFrameworks = false;
let onMountCallbacks = new Set();
let isMounted = false;
let frameworkIdsSelected = $state(new SvelteSet());
let snippetsByFrameworkId = $state(new SvelteMap());
let frameworkIdsSelectedInitialized = $state(false);
let isVersusFrameworks = $state(false);
let onMountCallbacks = $state(new SvelteSet());
let isMounted = $state(false);
const siteTitle = "Component Party";
const unsubscribeCurrentRoute = currentRoute.subscribe(($currentRoute) => {
@@ -65,7 +67,7 @@
const versusFrameworks = handleVersus($currentRoute.params.versus);
if (versusFrameworks) {
isVersusFrameworks = true;
frameworkIdsSelected = new Set(versusFrameworks.map((f) => f.id));
frameworkIdsSelected = new SvelteSet(versusFrameworks.map((f) => f.id));
frameworkIdsSelectedInitialized = true;
document.title = `${versusFrameworks
.map((f) => f.title)
@@ -114,7 +116,7 @@
frameworkIdsSelectedOnInit = ["react", "svelte4"];
}
frameworkIdsSelected = new Set(frameworkIdsSelectedOnInit);
frameworkIdsSelected = new SvelteSet(frameworkIdsSelectedOnInit);
frameworkIdsSelectedInitialized = true;
}
@@ -142,22 +144,16 @@
saveFrameworkIdsSelectedOnStorage();
}
$: {
async function importFrameworkSnippets(frameworkIds) {
for (const frameworkId of frameworkIds) {
if (!snippetsByFrameworkId.has(frameworkId)) {
const frameworkSnippets = (
await snippetsImporterByFrameworkId[frameworkId]()
).default;
snippetsByFrameworkId.set(frameworkId, frameworkSnippets);
}
$effect(async () => {
for (const frameworkId of [...frameworkIdsSelected]) {
if (!snippetsByFrameworkId.has(frameworkId)) {
const frameworkSnippets = (
await snippetsImporterByFrameworkId[frameworkId]()
).default;
snippetsByFrameworkId.set(frameworkId, frameworkSnippets);
}
snippetsByFrameworkId = snippetsByFrameworkId;
}
importFrameworkSnippets([...frameworkIdsSelected]);
}
});
const MAX_FRAMEWORK_NB_INITIAL_DISPLAYED = 10;
@@ -168,17 +164,19 @@
const FRAMEWORKS_MORE = FRAMEWORKS.slice(MAX_FRAMEWORK_NB_INITIAL_DISPLAYED);
$: frameworksSelected = [...frameworkIdsSelected].map(matchFrameworkId);
$: frameworksNotSelected = FRAMEWORKS_INITIAL_DISPLAYED.filter(
(f) => !frameworkIdsSelected.has(f.id)
const frameworksSelected = $derived(
[...frameworkIdsSelected].map(matchFrameworkId)
);
$: frameworksMoreNotSelected = FRAMEWORKS_MORE.filter(
(f) => !frameworkIdsSelected.has(f.id)
const frameworksNotSelected = $derived(
FRAMEWORKS_INITIAL_DISPLAYED.filter((f) => !frameworkIdsSelected.has(f.id))
);
let showBonusFrameworks = false;
const frameworksMoreNotSelected = $derived(
FRAMEWORKS_MORE.filter((f) => !frameworkIdsSelected.has(f.id))
);
let showBonusFrameworks = $state(false);
function handleVersus(versus) {
const fids = versus.split("-vs-");
@@ -219,7 +217,7 @@
? "border-blue-900"
: "opacity-70 border-opacity-50 border-gray-700"
)}
on:click={() => {
onclick={() => {
toggleFrameworkId(framework.id);
if (isVersusFrameworks && $currentRoute.path !== "/") {
navigate("/");
@@ -239,18 +237,19 @@
? "border-blue-500"
: "opacity-70"
)}
on:click={() => toggleFrameworkId(framework.id)}
onclick={() => toggleFrameworkId(framework.id)}
>
<FrameworkLabel id={framework.id} size={15} />
</button>
{/each}
{:else if frameworksMoreNotSelected.length > 0}
<button
title="more"
title="show more frameworks"
class="opacity-70 text-sm flex-shrink-0 rounded border border-gray-700 px-3 py-1 border-opacity-50 bg-gray-900 hover:bg-gray-800 transition-all mr-2"
on:click={() => {
onclick={() => {
showBonusFrameworks = !showBonusFrameworks;
}}
aria-label="show more frameworks"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -270,12 +269,12 @@
{/if}
</div>
<main class="relative pt-10">
<main class="relative pt-6">
<div>
{#if frameworkIdsSelected.size === 0}
<div class="space-y-4">
<div class="flex justify-center">
<ArrowUpIcon class="size-6 animate-bounce" />
<div class="i-heroicons:arrow-up size-6 animate-bounce"></div>
</div>
<div class="flex justify-center">
<p
@@ -290,12 +289,13 @@
</div>
</div>
{:else}
<div
class="max-w-full prose prose-sm prose-invert prose-h1:font-semibold prose-h2:font-medium prose-h3:font-medium prose-h1:scroll-mt-[5rem] prose-pre:mt-0 prose-pre:text-base prose-h2:scroll-mt-[5rem]"
>
<div class="space-y-20">
{#each sections as section}
<div class="px-6 md:px-14 lg:px-20 max-w-full">
<h1 id={section.sectionId} class="header-anchor">
<h1
id={section.sectionId}
class="header-anchor text-2xl font-bold"
>
{section.title}
<a
href={"#" + section.sectionId}
@@ -306,136 +306,140 @@
</a>
</h1>
{#each snippets.filter((s) => s.sectionId === section.sectionId) as snippet}
{@const snippetPathId =
section.sectionId + "." + snippet.snippetId}
<div id={snippetPathId} data-snippet-id={snippetPathId}>
<h2
class="header-anchor sticky py-2 top-[2.9531rem] z-10 bg-[var(--bg-color)]"
>
{snippet.title}
<a
href={"#" + snippetPathId}
aria-hidden="true"
tabindex="-1"
<div class="space-y-8 mt-2">
{#each snippets.filter((s) => s.sectionId === section.sectionId) as snippet}
{@const snippetPathId =
section.sectionId + "." + snippet.snippetId}
<div id={snippetPathId} data-snippet-id={snippetPathId}>
<h2
class="header-anchor sticky py-2 top-[2.94rem] z-10 bg-[var(--bg-color)] font-semibold text-xl"
>
#
</a>
</h2>
{#if frameworkIdsSelectedInitialized}
<div
class="grid grid-cols-1 2xl:grid-cols-2 gap-x-10"
style="margin-top: 1rem;"
>
{#each [...frameworkIdsSelected] as frameworkId (frameworkId)}
{@const framework = matchFrameworkId(frameworkId)}
{@const frameworkSnippet = snippetsByFrameworkId
.get(frameworkId)
?.find((s) => s.snippetId === snippet.snippetId)}
{#if frameworkSnippet}
<div style:margin-top="0rem" style:order="0">
<div
class="flex justify-between items-center space-x-3"
>
<h3
style="margin-top: 0rem; margin-bottom: 0rem;"
{snippet.title}
<a
href={"#" + snippetPathId}
aria-hidden="true"
tabindex="-1"
>
#
</a>
</h2>
{#if frameworkIdsSelectedInitialized}
<div
class="grid grid-cols-1 2xl:grid-cols-2 gap-10 mt-4"
>
{#each [...frameworkIdsSelected] as frameworkId (frameworkId)}
{@const framework = matchFrameworkId(frameworkId)}
{@const frameworkSnippet = snippetsByFrameworkId
.get(frameworkId)
?.find((s) => s.snippetId === snippet.snippetId)}
{#if frameworkSnippet}
<div style:margin-top="0rem" style:order="0">
<div
class="flex justify-between items-center space-x-3"
>
<FrameworkLabel id={framework.id} />
</h3>
<div class="flex items-center space-x-3">
{#if frameworkSnippet.playgroundURL}
<h3
style="margin-top: 0rem; margin-bottom: 0rem;"
>
<FrameworkLabel id={framework.id} />
</h3>
<div class="flex items-center space-x-3">
{#if frameworkSnippet.playgroundURL}
<a
href={frameworkSnippet.playgroundURL}
target="_blank"
rel="noreferrer"
aria-label={`Open playground for ${framework.title}`}
>
<button
class="opacity-50 hover:opacity-100 bg-gray-800 hover:bg-gray-700 py-1 px-1.5 rounded transition-all"
title={`Open playground for ${framework.title}`}
aria-label={`Open playground for ${framework.title}`}
>
<div
class="i-heroicons:play size-4"
></div>
</button>
</a>
{/if}
<a
href={frameworkSnippet.playgroundURL}
href={frameworkSnippet.snippetEditHref}
target="_blank"
rel="noreferrer"
aria-label="Edit on Github"
>
<button
class="opacity-50 hover:opacity-100 bg-gray-800 hover:bg-gray-700 py-1 px-1.5 rounded transition-all"
title={`Open playground for ${framework.title}`}
aria-label={`Open playground for ${framework.title}`}
title="Edit on Github"
aria-label="Edit on Github"
>
<PlayIcon class="h-4 w-4" />
<div
class="i-heroicons:pencil size-4"
></div>
</button>
</a>
{/if}
<a
href={frameworkSnippet.snippetEditHref}
target="_blank"
rel="noreferrer"
>
<button
class="opacity-50 hover:opacity-100 bg-gray-800 hover:bg-gray-700 py-1 px-1.5 rounded transition-all"
title="Edit on Github"
aria-label="Edit on Github"
>
<PencilIcon class="h-4 w-4" />
</button>
</a>
</div>
</div>
<div class="mt-2">
{#if frameworkSnippet.markdownFiles.length > 0}
<div class="space-y-2">
{#each frameworkSnippet.markdownFiles as markdownFile}
<div>
{@html markdownFile.contentHtml}
</div>
{/each}
</div>
{:else if frameworkSnippet.files.length > 0}
<CodeEditor files={frameworkSnippet.files} />
{:else}
<div
class="bg-gray-800 text-white rounded-md mx-auto"
>
<div class="text-center py-8 px-4 sm:px-6">
<div>
<span
class="block text-2xl tracking-tight font-bold"
>
Missing snippet
</span>
<span
class="block text-lg mt-1 font-semibold space-x-1"
>
<span>
Help us to improve Component Party
</span>
<img
src="/popper.svg"
alt="logo"
class="size-5 m-0 inline-block"
/>
</span>
</div>
<div class="mt-6 flex justify-center">
<div
class="inline-flex rounded-md shadow"
>
<a
class="inline-flex space-x-2 items-center justify-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-[#161b22] hover:bg-[#161b22]/80 no-underline"
href={frameworkSnippet.snippetEditHref}
</div>
<div class="mt-2">
{#if frameworkSnippet.files.length > 0}
<CodeEditor
files={frameworkSnippet.files}
/>
{:else}
<div
class="bg-gray-800 text-white rounded-md mx-auto"
>
<div
class="text-center py-8 px-4 sm:px-6"
>
<div>
<span
class="block text-2xl tracking-tight font-bold"
>
<button
class="flex items-center space-x-3"
Missing snippet
</span>
<span
class="block text-lg mt-1 font-semibold space-x-1"
>
<span>
Help us to improve Component Party
</span>
<img
src="/popper.svg"
alt="logo"
class="size-5 m-0 inline-block"
/>
</span>
</div>
<div class="mt-6 flex justify-center">
<div
class="inline-flex rounded-md shadow"
>
<a
class="inline-flex space-x-2 items-center justify-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-[#161b22] hover:bg-[#161b22]/80 no-underline"
href={frameworkSnippet.snippetEditHref}
>
<span>Contribute on Github</span>
<GithubIcon class="h-5 w-5" />
</button>
</a>
<button
class="flex items-center space-x-3"
>
<span>Contribute on Github</span
>
<GithubIcon class="h-5 w-5" />
</button>
</a>
</div>
</div>
</div>
</div>
</div>
{/if}
{/if}
</div>
</div>
</div>
{/if}
{/each}
</div>
{/if}
</div>
{/each}
{/if}
{/each}
</div>
{/if}
</div>
{/each}
</div>
</div>
{/each}
</div>

View File

@@ -3,7 +3,7 @@
import { writable } from "svelte/store";
import { createRouter } from "radix3";
export let routes = [];
let { routes = [] } = $props();
const router = createRouter({
routes: routes.reduce((acc, route) => {
@@ -26,10 +26,11 @@
currentRoute.set(routePayload);
window.history.pushState(state, "", path);
} else if (typeof routePayload.component === "function") {
routePayload.component().then((module) => {
currentRoute.set({ ...routePayload, component: module.default });
window.history.pushState(state, "", path);
currentRoute.set({
...routePayload,
component: routePayload.component,
});
window.history.pushState(state, "", path);
} else {
console.error("Invalid route component");
}
@@ -69,4 +70,6 @@
});
</script>
<svelte:component this={$currentRoute.component} />
{#if $currentRoute.component}
{@render $currentRoute.component()}
{/if}

View File

@@ -1,17 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@supports (backdrop-filter: blur(10px)) {
[class*="bg-"].backdrop-blur {
background-color: rgba(0, 0, 0, 0);
}
}
.shiki {
@apply text-[1.05rem];
}
.no-scroll {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */

View File

@@ -1,40 +1,40 @@
<script>
import NotificationCenter from "@veljs/svelte/NotificationCenter.svelte";
import TransitionWithClass from "@veljs/svelte/TransitionWithClass.svelte";
import { CheckCircleIcon, XMarkIcon } from "heroiconsvelte/24/outline";
import NotificationCenter from "./NotificationCenter.svelte";
import TransitionWithClass from "./TransitionWithClass.svelte";
</script>
<NotificationCenter zIndex={100} let:notification>
{@const { title } = notification}
<TransitionWithClass
class="pointer-events-auto overflow-hidden rounded-lg bg-[#181622] border border-[#33323e] shadow-lg ring-1 ring-black ring-opacity-5"
enter="transform ease-out duration-200 transition"
enterFrom="translate-y-2 opacity-0 translate-y-0 translate-x-2"
enterTo="translate-y-0 opacity-100 translate-x-0"
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<CheckCircleIcon class="h-6 w-6 text-green-400" />
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm font-medium">
{title}
</p>
</div>
<div class="ml-4 flex flex-shrink-0">
<button
class="inline-flex rounded-md bg-transparent focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
on:click={() => notification.close()}
>
<span class="sr-only">Close</span>
<XMarkIcon class="h-5 w-5" />
</button>
<NotificationCenter zIndex={100}>
{#snippet notificationContainer({ title, close })}
<TransitionWithClass
class="pointer-events-auto overflow-hidden rounded-lg bg-[#181622] border border-[#33323e] shadow-lg ring-1 ring-black ring-opacity-5"
enter="transform ease-out duration-200 transition"
enterFrom="translate-y-2 opacity-0 translate-y-0 translate-x-2"
enterTo="translate-y-0 opacity-100 translate-x-0"
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<div class="i-heroicons:check-circle size-6 text-green-400"></div>
</div>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm font-medium">
{title}
</p>
</div>
<div class="ml-4 flex flex-shrink-0">
<button
class="inline-flex rounded-md bg-transparent focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
onclick={close}
>
<span class="sr-only">Close</span>
<div class="i-heroicons:x-mark size-5"></div>
</button>
</div>
</div>
</div>
</div>
</TransitionWithClass>
</TransitionWithClass>
{/snippet}
</NotificationCenter>

View File

@@ -4,7 +4,7 @@
import { onMount, onDestroy } from "svelte";
import throttle from "../lib/throttle.js";
let largestVisibleSnippetId = null;
let largestVisibleSnippetId = $state(null);
function getLargestElement(elements) {
let largestArea = 0;
@@ -84,13 +84,14 @@
</a>
<ul>
{#each snippets.filter((s) => s.sectionId === section.sectionId) as snippet}
{@const snippetPathId =
section.sectionId + "." + snippet.snippetId}
<li>
<a
href={`#${section.sectionId + "." + snippet.snippetId}`}
href={`#${snippetPathId}`}
class={c(
"inline-block w-full py-1.5 px-4 text-white font-medium hover:bg-gray-800 rounded hover:opacity-100 transition-opacity",
section.sectionId + "." + snippet.snippetId ===
largestVisibleSnippetId
snippetPathId === largestVisibleSnippetId
? "bg-gray-800 opacity-70"
: "opacity-50"
)}

View File

@@ -1,16 +1,17 @@
<script>
import c from "classnames";
import { notifications } from "@veljs/svelte/NotificationCenter.svelte";
import { ClipboardDocumentIcon } from "heroiconsvelte/24/outline";
import { notifications } from "./NotificationCenter.svelte";
import copyToClipboard from "../lib/copyToClipboard.js";
export let files = [];
const { files = [] } = $props();
let codeSnippetEl;
let codeSnippetEl = $state();
let filenameSelected = files.length > 0 && files[0]?.fileName;
$: snippet =
filenameSelected && files.find((s) => s.fileName === filenameSelected);
let filenameSelected = $state(files.length > 0 && files[0]?.fileName);
const snippet = $derived(
filenameSelected && files.find((s) => s.fileName === filenameSelected)
);
function copySnippet() {
if (codeSnippetEl) {
@@ -26,10 +27,10 @@
{#each files as file (file.fileName)}
<button
class={c(
"bg-[#0d1117] py-1.5 px-3 font-medium flex-shrink-0 text-xs rounded-t inline-block",
"bg-[#0d1117] py-1.5 px-3 flex-shrink-0 text-xs rounded-t inline-block",
filenameSelected !== file.fileName && "opacity-60"
)}
on:click={() => {
onclick={() => {
filenameSelected = file.fileName;
}}
>
@@ -39,7 +40,7 @@
</div>
<div class="relative group">
<div bind:this={codeSnippetEl} class="bg-[#0d1117]">
<div bind:this={codeSnippetEl} class="bg-[#0d1117] px-4 py-3 text-sm">
{@html snippet.contentHtml}
</div>
<div
@@ -50,9 +51,9 @@
class="px-1.5 bg-[#0d1117] py-1 rounded border opacity-60 hover:opacity-90"
title="Copy to clipboard"
aria-label="Copy to clipboard"
on:click={copySnippet}
onclick={copySnippet}
>
<ClipboardDocumentIcon class="h-5 w-5" />
<div class="i-heroicons:clipboard-document size-5"></div>
</button>
</div>
</div>

View File

@@ -1,10 +1,9 @@
<script>
import FRAMEWORKS from "../../frameworks.mjs";
export let id;
export let size = 20;
let { id, size = 20 } = $props();
const framework = FRAMEWORKS.find((f) => f.id === id);
const framework = $derived(FRAMEWORKS.find((f) => f.id === id));
const baseURL = import.meta.env.DEV
? "/"

View File

@@ -1,5 +1,9 @@
<script>
const props = $props();
</script>
<svg
{...$$props}
{...props}
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
focusable="false"

Before

Width:  |  Height:  |  Size: 885 B

After

Width:  |  Height:  |  Size: 929 B

View File

@@ -4,13 +4,12 @@
import GithubIcon from "./GithubIcon.svelte";
const REPOSITORY_PATH = "matschik/component-party.dev";
const DURATION_2_MIN = 1000 * 60 * 2;
const STAR_COUNT_DURATION_EXPIRATION = DURATION_2_MIN;
const STAR_COUNT_EXPIRES_IN_MS = 1000 * 60 * 2;
const starCountStorage = createLocaleStorage("github-star-count");
let starCount = 0;
let isFetchingStarCount = false;
let starCount = $state(0);
let isFetchingStarCount = $state(false);
async function getRepoStarCount() {
const starCountStorageData = starCountStorage.getJSON();
@@ -18,7 +17,7 @@
starCount = starCountStorageData.value;
if (
starCountStorageData.fetchedAt >
Date.now() - STAR_COUNT_DURATION_EXPIRATION
Date.now() - STAR_COUNT_EXPIRES_IN_MS
) {
return;
}
@@ -64,16 +63,16 @@
href={`https://github.com/${REPOSITORY_PATH}`}
target="_blank"
aria-label={`Star ${REPOSITORY_PATH} on GitHub`}
on:click={onButtonClick}
onclick={onButtonClick}
>
<span
class="flex items-center border-[#373b43] px-3 sm:space-x-2 sm:border-r"
>
<span class="flex items-center px-3 sm:space-x-2">
<GithubIcon class="size-[1.3rem] sm:size-[1.1rem]" />
<span class="hidden sm:inline">Star</span>
</span>
{#if isFetchingStarCount || starCount !== 0}
<div class="hidden h-full items-center justify-center px-3 sm:flex">
<div
class="hidden h-full items-center justify-center px-3 sm:flex border-[#373b43] sm:border-l"
>
{#if isFetchingStarCount && starCount === 0}
<svg
class="animate-spin size-4 mx-1"

View File

@@ -1,10 +1,9 @@
<script>
import { LinkIcon } from "heroiconsvelte/24/outline";
import { notifications } from "@veljs/svelte/NotificationCenter.svelte";
import { notifications } from "./NotificationCenter.svelte";
import GithubStarButton from "./GithubStarButton.svelte";
import copyToClipboard from "../lib/copyToClipboard.js";
export let frameworksSelected = [];
let { frameworksSelected = [] } = $props();
function copyShareLink() {
if (frameworksSelected.length === 0) {
@@ -37,9 +36,9 @@
type="button"
class="flex items-center space-x-2 rounded border border-gray-700 border-opacity-50 bg-gray-900 px-3 py-1 text-sm text-white transition-all hover:bg-gray-800"
aria-label="Copy framework selection link"
on:click={copyShareLink}
onclick={copyShareLink}
>
<LinkIcon class="size-[1.3rem] sm:size-[1.1rem]" />
<div class="i-heroicons:link size-[1.3rem] sm:size-[1.1rem]"></div>
<span class="hidden sm:inline">Share</span>
</button>
{/if}

View File

@@ -0,0 +1,96 @@
<script module>
import { writable } from "svelte/store";
const { subscribe, update } = writable([]);
const DISMISS_TIMEOUT_DEFAULT = 4 * 1000;
export const notifications = {
subscribe,
show(notification) {
notification = {
...notification,
dismissAfter: DISMISS_TIMEOUT_DEFAULT,
close() {
notifications.dismiss(this);
},
};
update((notifications) => [notification, ...notifications]);
},
dismiss(notification) {
update((notifications) =>
notifications.filter((t) => t !== notification)
);
},
dismissAll() {
update(() => []);
},
};
export function addNotificationsMethod(method, cb) {
notifications[method] = (...args) => {
const notification = cb(...args);
notifications.show(notification);
};
}
</script>
<script>
let { zIndex = 20, notificationContainer } = $props();
function dismissAfter(_, notification) {
notification.dismissAfter &&
setTimeout(
() => notifications.dismiss(notification),
notification.dismissAfter
);
}
</script>
<div class="notifications-container" style={`z-index: ${zIndex};`}>
<ul class="notifications-list space-y-4">
{#each $notifications as notification (notification)}
<li use:dismissAfter={notification}>
{@render notificationContainer(notification)}
</li>
{/each}
</ul>
</div>
<style>
.notifications-container {
position: fixed;
top: 0;
right: 0;
display: flex;
align-items: flex-end;
margin: 2.5rem;
}
@media (min-width: 640px) {
.notifications-container {
margin: 1.5rem;
align-items: flex-start;
}
}
.notifications-list {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
@media (min-width: 640px) {
.notifications-list {
align-items: flex-end;
}
}
.space-y-4 > * + * {
margin-top: 1rem;
}
li {
width: 20rem;
}
</style>

View File

@@ -0,0 +1,71 @@
<script>
let {
enter = "transition ease-out duration-100",
enterFrom = "transform opacity-0 scale-95",
enterTo = "transform opacity-100 scale-100",
leave = "transition ease-in duration-75",
leaveFrom = "transform opacity-100 scale-100",
leaveTo = "transform opacity-0 scale-95",
class: className = undefined,
children,
} = $props();
const onEnter = createTransitionWithClasses("enter", {
from: enter,
active: enterFrom,
to: enterTo,
});
const onLeave = createTransitionWithClasses("leave", {
from: leave,
active: leaveTo,
to: leaveFrom,
});
function createTransitionWithClasses(type, { from, active, to }) {
if (!["enter", "leave"].includes(type)) {
throw Error(`Transition type must be 'enter' or 'leave', not ${type}`);
}
const durationClass = from
.split(" ")
.find((cssClass) => cssClass.startsWith("duration-"));
const duration = Number(durationClass.split("-").pop());
if (!durationClass || Number.isNaN(duration)) {
throw Error(
`First classes need to have a valid duration class as: duration-X. Found '${durationClass}'`
);
}
return (node) => {
if (type === "leave") {
node.classList.remove(...enter.split(" "));
node.classList.remove(...enterFrom.split(" "));
node.classList.remove(...enterTo.split(" "));
}
return {
duration,
tick: (t) => {
if (
(type === "enter" && t >= 0 && t !== 1) ||
(type === "leave" && t < 1 && t !== 0)
) {
node.classList.remove(...from.split(" "));
node.classList.add(...from.split(" "));
node.classList.remove(...active.split(" "));
node.classList.add(...active.split(" "));
} else {
node.classList.remove(...active.split(" "));
node.classList.add(...to.split(" "));
}
},
};
};
}
</script>
<div in:onEnter out:onLeave class={className}>
{@render children()}
</div>

View File

@@ -1,8 +1,9 @@
import App from "./App.svelte";
import "./app.css";
import { mount } from "svelte";
import "@unocss/reset/tailwind.css";
import "virtual:uno.css";
const app = new App({
target: document.getElementById("app"),
});
const app = mount(App, { target: document.getElementById("app") });
export default app;

8
svelte.config.js Normal file
View File

@@ -0,0 +1,8 @@
export default {
compilerOptions: {
runes: true,
},
vitePlugin: {
inspector: false, // set to true when inspector is compatible svelte 5
},
};

View File

@@ -1,8 +0,0 @@
module.exports = {
content: [
"./src/**/*.{html,js,svelte,ts,eta}",
"./index.html",
"build/template/*.{html,eta}",
],
plugins: [require("@tailwindcss/typography")],
};

View File

@@ -15,5 +15,6 @@
"paths": {
"@/*": ["src/*"]
}
}
},
"exclude": ["node_modules", "dist"]
}

11
uno.config.ts Normal file
View File

@@ -0,0 +1,11 @@
import presetWind from "@unocss/preset-wind";
import { defineConfig } from "unocss";
import { presetTypography } from "unocss";
import presetIcons from "@unocss/preset-icons";
export default defineConfig({
content: {
filesystem: ["build/template/*.html"],
},
presets: [presetIcons(), presetWind(), presetTypography()],
});

View File

@@ -1,13 +1,13 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import sveltePreprocess from "svelte-preprocess";
import fs from "node:fs/promises";
import path from "node:path";
import { Eta } from "eta";
import { minify as htmlMinify } from "html-minifier-terser";
import FRAMEWORKS from "./frameworks.mjs";
import pluginGenerateFrameworkContent from "./build/generateContentVitePlugin.js";
import UnoCSS from "unocss/vite";
import { svelteInspector } from "@sveltejs/vite-plugin-svelte-inspector";
// @TODO: sitemap
const footerNavigation = [
@@ -69,6 +69,7 @@ export default defineConfig({
plugins: [
pluginGenerateFrameworkContent(),
svelte(),
svelteInspector(), // https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/inspector.md
generateHtmlPagesPlugin([
...footerLinks.map((link) => ({
outputPath: `${link.url}.html`,
@@ -84,13 +85,9 @@ export default defineConfig({
templateData: templateDataDefaults,
},
]),
UnoCSS(),
],
ignore: ["content"],
preprocess: [
sveltePreprocess({
postcss: true,
}),
],
});
async function generateHtmlPagesPlugin(pages) {
@@ -98,7 +95,7 @@ async function generateHtmlPagesPlugin(pages) {
const template = {
footer: await fs.readFile(
path.resolve(__dirname, "build/template/footer.eta"),
path.resolve(__dirname, "build/template/footer.html"),
"utf8"
),
};