mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-08 21:07:45 +00:00
update homepage
This commit is contained in:
@@ -25,7 +25,8 @@
|
|||||||
"@sentry/sveltekit": "^8.12.0",
|
"@sentry/sveltekit": "^8.12.0",
|
||||||
"embla-carousel-svelte": "^8.1.5",
|
"embla-carousel-svelte": "^8.1.5",
|
||||||
"embla-carousel-wheel-gestures": "^8.0.1",
|
"embla-carousel-wheel-gestures": "^8.0.1",
|
||||||
"h3": "^1.12.0"
|
"h3": "^1.12.0",
|
||||||
|
"remeda": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@appwrite.io/console": "^0.6.2",
|
"@appwrite.io/console": "^0.6.2",
|
||||||
|
|||||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -20,6 +20,9 @@ importers:
|
|||||||
h3:
|
h3:
|
||||||
specifier: ^1.12.0
|
specifier: ^1.12.0
|
||||||
version: 1.12.0
|
version: 1.12.0
|
||||||
|
remeda:
|
||||||
|
specifier: ^2.3.0
|
||||||
|
version: 2.3.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@appwrite.io/console':
|
'@appwrite.io/console':
|
||||||
specifier: ^0.6.2
|
specifier: ^0.6.2
|
||||||
@@ -3280,6 +3283,9 @@ packages:
|
|||||||
regenerator-runtime@0.14.1:
|
regenerator-runtime@0.14.1:
|
||||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||||
|
|
||||||
|
remeda@2.3.0:
|
||||||
|
resolution: {integrity: sha512-J9djcCyFL9D1ZGnfiNJ6MMhcUT8BmNCT+dj2SApdrTNz+CxqW1dScPVaDCopivHTShiYXh4sRSPYKMzlGrgyQw==}
|
||||||
|
|
||||||
require-directory@2.1.1:
|
require-directory@2.1.1:
|
||||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -3723,6 +3729,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
type-fest@4.21.0:
|
||||||
|
resolution: {integrity: sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
typescript@5.5.2:
|
typescript@5.5.2:
|
||||||
resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
|
resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
@@ -7343,6 +7353,10 @@ snapshots:
|
|||||||
|
|
||||||
regenerator-runtime@0.14.1: {}
|
regenerator-runtime@0.14.1: {}
|
||||||
|
|
||||||
|
remeda@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
type-fest: 4.21.0
|
||||||
|
|
||||||
require-directory@2.1.1: {}
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
require-in-the-middle@7.3.0:
|
require-in-the-middle@7.3.0:
|
||||||
@@ -7856,6 +7870,8 @@ snapshots:
|
|||||||
|
|
||||||
type-fest@0.20.2: {}
|
type-fest@0.20.2: {}
|
||||||
|
|
||||||
|
type-fest@4.21.0: {}
|
||||||
|
|
||||||
typescript@5.5.2: {}
|
typescript@5.5.2: {}
|
||||||
|
|
||||||
uc.micro@2.1.0: {}
|
uc.micro@2.1.0: {}
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
let carousel: HTMLElement;
|
|
||||||
|
|
||||||
let gap = 32;
|
|
||||||
let scroll = 0;
|
|
||||||
|
|
||||||
let activeIndex = 0;
|
|
||||||
|
|
||||||
function calculateScrollAmount(prev = false) {
|
|
||||||
const direction = prev ? -1 : 1;
|
|
||||||
const carouselSize = carousel?.clientWidth;
|
|
||||||
const childSize = (carousel.childNodes[0] as HTMLUListElement)?.clientWidth + gap;
|
|
||||||
|
|
||||||
scroll = scroll || carouselSize;
|
|
||||||
|
|
||||||
const numberOfItems = Math.floor(carouselSize / childSize);
|
|
||||||
const overflow = scroll % childSize;
|
|
||||||
const amount = numberOfItems * childSize - overflow * direction;
|
|
||||||
scroll += amount * direction;
|
|
||||||
return amount * direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
function next() {
|
|
||||||
carousel.scrollBy({
|
|
||||||
left: calculateScrollAmount(),
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
activeIndex++;
|
|
||||||
}
|
|
||||||
function prev() {
|
|
||||||
carousel.scrollBy({
|
|
||||||
left: calculateScrollAmount(true),
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
activeIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isEnd = false;
|
|
||||||
let isStart = true;
|
|
||||||
|
|
||||||
function handleScroll() {
|
|
||||||
isStart = carousel.scrollLeft <= 0;
|
|
||||||
isEnd = Math.ceil(carousel.scrollLeft + carousel.offsetWidth) >= carousel.scrollWidth;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="web-carousel">
|
|
||||||
{#if !isStart}
|
|
||||||
<button class="web-carousel-button web-carousel-button-start" on:click={() => prev()}>
|
|
||||||
<span class="web-icon-arrow-left" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{#if !isEnd}
|
|
||||||
<button class="web-carousel-button web-carousel-button-end" on:click={() => next()}>
|
|
||||||
<span class="web-icon-arrow-right" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="web-carousel-overflow">
|
|
||||||
<ul
|
|
||||||
class="web-carousel-list"
|
|
||||||
style:gap="{gap}px"
|
|
||||||
bind:this={carousel}
|
|
||||||
on:scroll={handleScroll}
|
|
||||||
>
|
|
||||||
<slot {activeIndex} />
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
export let isActive: boolean = false;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<li class="web-carousel-item" class:is-main={isActive}>
|
|
||||||
<slot />
|
|
||||||
</li>
|
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
import type { FuseResult } from 'fuse.js';
|
import type { FuseResult } from 'fuse.js';
|
||||||
|
|
||||||
// Reexport your entry components here
|
// Reexport your entry components here
|
||||||
export { default } from './SvelteFuse.svelte';
|
export { default as Fuse } from './Fuse.svelte';
|
||||||
|
|
||||||
export type ResultType<T> = FuseResult<T>[];
|
export type ResultType<T> = FuseResult<T>[];
|
||||||
|
|
||||||
import CarouselRoot from './Carousel.svelte';
|
|
||||||
import CarouselSlide from './CarouselSlide.svelte';
|
|
||||||
|
|
||||||
export const Carousel = Object.assign({
|
|
||||||
Root: CarouselRoot,
|
|
||||||
Slide: CarouselSlide
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Embla } from '$lib/components/embla';
|
import { Embla } from '$lib/components/embla';
|
||||||
|
import FooterNav from '$lib/components/FooterNav.svelte';
|
||||||
|
import MainFooter from '$lib/components/MainFooter.svelte';
|
||||||
import ProductsGrid from '$lib/components/ProductsGrid.svelte';
|
import ProductsGrid from '$lib/components/ProductsGrid.svelte';
|
||||||
import { Main } from '$lib/layouts';
|
import { Main } from '$lib/layouts';
|
||||||
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
|
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
|
||||||
import { TITLE_SUFFIX } from '$routes/titles';
|
import type { Integration } from '$routes/integrations/+page';
|
||||||
|
|
||||||
import FooterNav from '../../../lib/components/FooterNav.svelte';
|
export let title: Integration['title'];
|
||||||
import MainFooter from '../../../lib/components/MainFooter.svelte';
|
export let images: Integration['images'];
|
||||||
|
export let isNew: Integration['isNew'];
|
||||||
|
export let isPartner: Integration['isPartner'];
|
||||||
|
export let author: Integration['author'];
|
||||||
|
export let category: Integration['category'];
|
||||||
|
|
||||||
const title = 'Integrations' + TITLE_SUFFIX;
|
//const title = 'Integrations' + TITLE_SUFFIX;
|
||||||
const description = DEFAULT_DESCRIPTION;
|
const description = DEFAULT_DESCRIPTION;
|
||||||
const ogImage = DEFAULT_HOST + '/images/open-graph/website.png';
|
const ogImage = DEFAULT_HOST + '/images/open-graph/website.png';
|
||||||
|
|
||||||
const slides = Array.from({ length: 4 });
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@@ -45,12 +49,12 @@
|
|||||||
<span>Back to catalog</span>
|
<span>Back to catalog</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{#if slides.length > 1}
|
{#if images.length > 1}
|
||||||
<Embla.Root>
|
<Embla.Root>
|
||||||
{#each slides as _}
|
{#each images as slide}
|
||||||
<Embla.Slide>
|
<Embla.Slide>
|
||||||
<img
|
<img
|
||||||
src="/images/blog/ai-announcement.png"
|
src={slide}
|
||||||
class="web-u-media-ratio-16-9"
|
class="web-u-media-ratio-16-9"
|
||||||
alt="cover"
|
alt="cover"
|
||||||
width="472"
|
width="472"
|
||||||
@@ -65,7 +69,7 @@
|
|||||||
style:margin="0 auto"
|
style:margin="0 auto"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/blog/ai-announcement.png"
|
src={images[0]}
|
||||||
class="web-u-media-ratio-16-9"
|
class="web-u-media-ratio-16-9"
|
||||||
alt="cover"
|
alt="cover"
|
||||||
width="472"
|
width="472"
|
||||||
@@ -92,13 +96,13 @@
|
|||||||
height="40"
|
height="40"
|
||||||
/>
|
/>
|
||||||
<div class="u-flex u-cross-center u-flex-wrap u-gap-16">
|
<div class="u-flex u-cross-center u-flex-wrap u-gap-16">
|
||||||
<h1 class="web-title web-u-color-text-primary">Lorem ipsum</h1>
|
<h1 class="web-title web-u-color-text-primary">{title}</h1>
|
||||||
<a
|
<a
|
||||||
href="/blog/post/announcing-appwrite-new-ai-integrations"
|
href="/blog/post/announcing-appwrite-new-ai-integrations"
|
||||||
class="web-hero-banner-button"
|
class="web-hero-banner-button"
|
||||||
>
|
>
|
||||||
<span class="web-icon-star" aria-hidden="true"></span>
|
<span class="web-icon-star" aria-hidden="true"></span>
|
||||||
<span class="web-caption-500">New</span>
|
{#if isNew}<span class="web-caption-500">New</span>{/if}
|
||||||
<div class="web-hero-banner-button-sep"></div>
|
<div class="web-hero-banner-button-sep"></div>
|
||||||
<span class="web-caption-400 web-u-trim-1"
|
<span class="web-caption-400 web-u-trim-1"
|
||||||
>Developed by Appwrite</span
|
>Developed by Appwrite</span
|
||||||
@@ -109,31 +113,7 @@
|
|||||||
|
|
||||||
<div class="web-article">
|
<div class="web-article">
|
||||||
<div class="web-article-content">
|
<div class="web-article-content">
|
||||||
<p class="web-paragraph-md">
|
<slot />
|
||||||
The AI hype is real and will be around for many years to
|
|
||||||
come. In 2021 alone, AI startups worldwide raised nearly $50
|
|
||||||
billion in venture capital across approximately 1,500 deals,
|
|
||||||
reflecting a significant increase from previous years. So
|
|
||||||
it's no surprise that many of you are looking to build AI
|
|
||||||
powered applications. But that's easier said than done, as
|
|
||||||
building AI powered applications can be tricky. We don't
|
|
||||||
want it to be. That's why we're happy to share multiple AI
|
|
||||||
related announcements designed to enhance the Appwrite
|
|
||||||
experience and adapt to new possibilities for devs building
|
|
||||||
with Appwrite.
|
|
||||||
</p>
|
|
||||||
<p class="web-paragraph-md">
|
|
||||||
Lorem ipsum dolor sit amet consectetur. Neque id vel eros
|
|
||||||
sed ipsum. Nisi fames euismod egestas morbi massa eget. At
|
|
||||||
viverra nibh semper arcu in. Convallis etiam dui elit mauris
|
|
||||||
mattis netus sit. Auctor dolor porttitor viverra eget
|
|
||||||
egestas sem auctor suspendisse. Massa phasellus condimentum
|
|
||||||
nam ac quis quis duis risus. Ac ut aliquet tortor donec erat
|
|
||||||
ac. Enim a eros sit purus et lacinia. Faucibus nibh donec
|
|
||||||
auctor mattis enim. Elementum quam sed auctor lacus
|
|
||||||
tincidunt velit semper dui. Dolor commodo consectetur mollis
|
|
||||||
morbi metus arcu at.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,17 +121,19 @@
|
|||||||
<dl class="u-flex-vertical u-gap-20">
|
<dl class="u-flex-vertical u-gap-20">
|
||||||
<div class="u-flex u-main-space-between u-gap-8">
|
<div class="u-flex u-main-space-between u-gap-8">
|
||||||
<dt>Developed by</dt>
|
<dt>Developed by</dt>
|
||||||
<dd class="web-u-color-text-primary">Appwrite</dd>
|
<dd class="web-u-color-text-primary">{author.name}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="web-u-sep-block-end"></div>
|
<div class="web-u-sep-block-end"></div>
|
||||||
|
{#if isPartner}
|
||||||
<div class="u-flex u-main-space-between u-gap-8">
|
<div class="u-flex u-main-space-between u-gap-8">
|
||||||
<dt>Partner</dt>
|
<dt>Partner</dt>
|
||||||
<dd><div class="web-inline-tag">Verified</div></dd>
|
<dd><div class="web-inline-tag">Verified</div></dd>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="web-u-sep-block-end"></div>
|
<div class="web-u-sep-block-end"></div>
|
||||||
<div class="u-flex u-main-space-between u-gap-8">
|
<div class="u-flex u-main-space-between u-gap-8">
|
||||||
<dt>Category</dt>
|
<dt>Category</dt>
|
||||||
<dd class="web-u-color-text-primary">Category</dd>
|
<dd class="web-u-color-text-primary">{category}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
@@ -5,48 +5,24 @@
|
|||||||
import integrations from '../../integrations.json';
|
import integrations from '../../integrations.json';
|
||||||
import FooterNav from '$lib/components/FooterNav.svelte';
|
import FooterNav from '$lib/components/FooterNav.svelte';
|
||||||
import MainFooter from '$lib/components/MainFooter.svelte';
|
import MainFooter from '$lib/components/MainFooter.svelte';
|
||||||
import SvelteFuse from '$lib/integrations/SvelteFuse.svelte';
|
import { type ResultType, Fuse } from '$lib/integrations';
|
||||||
import type { ResultType } from '$lib/integrations';
|
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { autoHash } from '$lib/actions/autoHash';
|
import { autoHash } from '$lib/actions/autoHash';
|
||||||
|
import type { Integration } from './+page';
|
||||||
|
|
||||||
|
export let data;
|
||||||
|
|
||||||
const title = 'Integrations' + TITLE_SUFFIX;
|
const title = 'Integrations' + TITLE_SUFFIX;
|
||||||
const description = DEFAULT_DESCRIPTION;
|
const description = DEFAULT_DESCRIPTION;
|
||||||
const ogImage = DEFAULT_HOST + '/images/open-graph/website.png';
|
const ogImage = DEFAULT_HOST + '/images/open-graph/website.png';
|
||||||
|
|
||||||
// integration data
|
|
||||||
type Integrations = (typeof integrations)[number];
|
|
||||||
type Integration = Integrations['items'][number];
|
|
||||||
|
|
||||||
const getFeaturedIntegrations = () => {
|
|
||||||
const featuredIntegrations: Array<Integration> = [];
|
|
||||||
|
|
||||||
integrations.forEach((integration) => {
|
|
||||||
integration.items.forEach((item) => {
|
|
||||||
if (item.featured) {
|
|
||||||
featuredIntegrations.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return featuredIntegrations.slice(0, 4);
|
|
||||||
};
|
|
||||||
|
|
||||||
const featuredIntegrations = getFeaturedIntegrations();
|
|
||||||
|
|
||||||
// search functionality
|
// search functionality
|
||||||
let fuseOptions = {
|
let fuseOptions = {
|
||||||
keys: ['title'],
|
keys: ['title'],
|
||||||
threshold: 0.3
|
threshold: 0.3
|
||||||
};
|
};
|
||||||
|
|
||||||
// flatten the integrations into a single array for search
|
let result: ResultType<Integration> = [];
|
||||||
const list = integrations.reduce(
|
|
||||||
(acc, category) => acc.concat(category.items),
|
|
||||||
[] as Integration[]
|
|
||||||
);
|
|
||||||
|
|
||||||
let result: ResultType<(typeof list)[number]> = [];
|
|
||||||
|
|
||||||
let hasQuery: boolean;
|
let hasQuery: boolean;
|
||||||
let query = writable('');
|
let query = writable('');
|
||||||
@@ -56,25 +32,11 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// platform filters
|
// platform filters
|
||||||
const platforms = [
|
const platforms = ['All', ...data.platforms];
|
||||||
{
|
|
||||||
label: 'All',
|
|
||||||
value: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Cloud',
|
|
||||||
value: 'cloud'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Self-hosted',
|
|
||||||
value: 'self-hosted'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
let activePlatform = '';
|
let activePlatform = '';
|
||||||
|
|
||||||
// categories
|
// categories
|
||||||
const categories = integrations.map((integration) => integration.category).sort();
|
|
||||||
let activeCategory = '';
|
let activeCategory = '';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -96,7 +58,7 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<!-- binding for fuse -->
|
<!-- binding for fuse -->
|
||||||
<SvelteFuse {list} options={fuseOptions} bind:query={$query} bind:result />
|
<Fuse list={data.list} options={fuseOptions} bind:query={$query} bind:result />
|
||||||
<Main>
|
<Main>
|
||||||
<header class="web-u-sep-block-end u-padding-block-end-0 u-position-relative u-overflow-hidden">
|
<header class="web-u-sep-block-end u-padding-block-end-0 u-position-relative u-overflow-hidden">
|
||||||
<div class="web-container u-position-relative hero web-u-padding-block-end-0">
|
<div class="web-container u-position-relative hero web-u-padding-block-end-0">
|
||||||
@@ -161,14 +123,14 @@
|
|||||||
Platform
|
Platform
|
||||||
</h2>
|
</h2>
|
||||||
<ul class="u-flex u-flex-wrap u-gap-8" class:disabled={hasQuery}>
|
<ul class="u-flex u-flex-wrap u-gap-8" class:disabled={hasQuery}>
|
||||||
{#each platforms as { label, value }}
|
{#each platforms as platform}
|
||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
class="tag"
|
class="tag"
|
||||||
class:is-selected={activePlatform === value}
|
class:is-selected={activePlatform === platform}
|
||||||
class:active-tag={activePlatform === value}
|
class:active-tag={activePlatform === platform}
|
||||||
on:click={() => (activePlatform = value)}
|
on:click={() => (activePlatform = platform)}
|
||||||
>{label}</button
|
>{platform}</button
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -183,7 +145,7 @@
|
|||||||
<div class="u-position-relative is-only-tablet">
|
<div class="u-position-relative is-only-tablet">
|
||||||
<select class="web-input-text" bind:value={activeCategory}>
|
<select class="web-input-text" bind:value={activeCategory}>
|
||||||
<option disabled selected>Select</option>
|
<option disabled selected>Select</option>
|
||||||
{#each categories as category}
|
{#each data.categories as category}
|
||||||
<option value={category.toLowerCase()}
|
<option value={category.toLowerCase()}
|
||||||
>{category}</option
|
>{category}</option
|
||||||
>
|
>
|
||||||
@@ -199,7 +161,7 @@
|
|||||||
class="u-flex-vertical u-gap-16 is-only-desktop"
|
class="u-flex-vertical u-gap-16 is-only-desktop"
|
||||||
class:disabled={hasQuery}
|
class:disabled={hasQuery}
|
||||||
>
|
>
|
||||||
{#each categories as category}
|
{#each data.categories as category}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={`#${category.toLowerCase()}`}
|
href={`#${category.toLowerCase()}`}
|
||||||
@@ -237,7 +199,7 @@
|
|||||||
{#each result.map((d) => d.item) as item}
|
{#each result.map((d) => d.item) as item}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={item.url}
|
href={item.href}
|
||||||
class="web-card is-normal u-height-100-percent"
|
class="web-card is-normal u-height-100-percent"
|
||||||
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
|
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
|
||||||
>
|
>
|
||||||
@@ -279,13 +241,13 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul class="web-feature-grid">
|
<ul class="web-feature-grid">
|
||||||
{#each featuredIntegrations as item}
|
{#each data.featured as item}
|
||||||
<li
|
<li
|
||||||
class="web-feature-grid-item is-two-columns-desktop-only"
|
class="web-feature-grid-item is-two-columns-desktop-only"
|
||||||
>
|
>
|
||||||
<a class="web-overlay-item" href={item.url}>
|
<a class="web-overlay-item" href={item.href}>
|
||||||
<img
|
<img
|
||||||
src={item.image}
|
src={item.cover}
|
||||||
alt={item.title}
|
alt={item.title}
|
||||||
class="u-block web-u-media-ratio-16-9 web-u-media-cover"
|
class="u-block web-u-media-ratio-16-9 web-u-media-cover"
|
||||||
/>
|
/>
|
||||||
@@ -306,7 +268,7 @@
|
|||||||
class="web-u-color-text-primary"
|
class="web-u-color-text-primary"
|
||||||
>{item.author.name}</span
|
>{item.author.name}</span
|
||||||
>
|
>
|
||||||
{#if item.new}
|
{#if item.isNew}
|
||||||
<span
|
<span
|
||||||
class="web-inline-tag is-pink"
|
class="web-inline-tag is-pink"
|
||||||
>New</span
|
>New</span
|
||||||
@@ -326,33 +288,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#each integrations.map((integration) => {
|
{#each Object.entries(data.integrations) as [category, items]}
|
||||||
return { ...integration, items: activePlatform ? integration.items.filter((item) => item.platform.toLowerCase() === activePlatform.toLowerCase()) : integration.items };
|
{#if items.length > 0}
|
||||||
}) as integration (integration.category)}
|
|
||||||
{#if integration.items.length > 0}
|
|
||||||
<section
|
<section
|
||||||
class="l-max-size-list-cards-section u-flex-vertical u-gap-32"
|
class="l-max-size-list-cards-section u-flex-vertical u-gap-32"
|
||||||
id={integration.category.toLowerCase()}
|
id={category.toLowerCase()}
|
||||||
use:autoHash={() =>
|
use:autoHash={() =>
|
||||||
(activeCategory =
|
(activeCategory = category.toLowerCase())}
|
||||||
integration.category.toLowerCase())}
|
|
||||||
>
|
>
|
||||||
<header class="u-flex-vertical u-gap-4">
|
<header class="u-flex-vertical u-gap-4">
|
||||||
<h2 class="web-label web-u-color-text-primary">
|
<h2 class="web-label web-u-color-text-primary">
|
||||||
{integration.category}
|
{category}
|
||||||
</h2>
|
</h2>
|
||||||
<p class="web-description">
|
<p class="web-description">
|
||||||
{integration.description}
|
{category}
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<div
|
<div
|
||||||
class="l-max-size-list-cards u-flex-vertical u-gap-32"
|
class="l-max-size-list-cards u-flex-vertical u-gap-32"
|
||||||
>
|
>
|
||||||
<ul class="l-grid-1">
|
<ul class="l-grid-1">
|
||||||
{#each integration.items as item, index (`${item.title}-${index}`)}
|
{#each items as item, index (`${item.title}-${index}`)}
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href={item.url}
|
href={item.href}
|
||||||
class="web-card is-normal u-height-100-percent"
|
class="web-card is-normal u-height-100-percent"
|
||||||
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
|
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
|
||||||
>
|
>
|
||||||
@@ -386,7 +345,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
<a
|
<a
|
||||||
href={`#${integration.category.toLowerCase()}`}
|
href={`#${category.toLowerCase()}`}
|
||||||
class="l-float-button web-button is-text"
|
class="l-float-button web-button is-text"
|
||||||
>
|
>
|
||||||
<span>Show more</span>
|
<span>Show more</span>
|
||||||
|
|||||||
56
src/routes/integrations/+page.ts
Normal file
56
src/routes/integrations/+page.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { base } from '$app/paths';
|
||||||
|
import { groupBy } from 'remeda';
|
||||||
|
|
||||||
|
export type Integration = {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
featured?: boolean;
|
||||||
|
cover: string;
|
||||||
|
isNew?: boolean;
|
||||||
|
isPartner?: boolean;
|
||||||
|
platform: string[];
|
||||||
|
category: string;
|
||||||
|
author: {
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
username: string;
|
||||||
|
};
|
||||||
|
href: string;
|
||||||
|
images: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const load = () => {
|
||||||
|
const integrationsGlob = import.meta.glob('./**/*.markdoc', {
|
||||||
|
eager: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const categories: string[] = [];
|
||||||
|
const platforms: string[] = [];
|
||||||
|
|
||||||
|
const integrations = Object.entries(integrationsGlob).map(([filepath, integrationList]) => {
|
||||||
|
const { frontmatter } = integrationList as {
|
||||||
|
frontmatter: Integration;
|
||||||
|
};
|
||||||
|
|
||||||
|
const slug = filepath.replace('./', '').replace('/+page.markdoc', '');
|
||||||
|
const integrationName = slug.slice(slug.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
frontmatter.platform.map((p) => platforms.push(p));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...frontmatter,
|
||||||
|
href: `${base}/integrations/${integrationName}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
integrations: groupBy(
|
||||||
|
integrations.filter((i) => !i.featured),
|
||||||
|
(i) => i.category
|
||||||
|
),
|
||||||
|
list: integrations,
|
||||||
|
categories: new Set(categories),
|
||||||
|
platforms: new Set(platforms),
|
||||||
|
featured: integrations.filter((i) => i.featured)
|
||||||
|
};
|
||||||
|
};
|
||||||
23
src/routes/integrations/ai-int/+page.markdoc
Normal file
23
src/routes/integrations/ai-int/+page.markdoc
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
layout: integration
|
||||||
|
title: Integration Title
|
||||||
|
description: We're happy to announce Cloud is now available for everyone to signup.
|
||||||
|
date: 2023-05-02
|
||||||
|
featured: true
|
||||||
|
isPartner: true
|
||||||
|
isNew: true
|
||||||
|
cover: /images/blog/ai-announcement.png
|
||||||
|
category: AI
|
||||||
|
author:
|
||||||
|
avatar: '/images/community/avatars/souvik.png'
|
||||||
|
name: Souvik Sarkar
|
||||||
|
username: 'Jeet_2003'
|
||||||
|
platform:
|
||||||
|
- 'Cloud'
|
||||||
|
images:
|
||||||
|
- /images/blog/ai-announcement.png
|
||||||
|
- /images/blog/ai-announcement.png
|
||||||
|
- /images/blog/ai-announcement.png
|
||||||
|
---
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tristique aliquam sagittis. Nam luctus tincidunt risus, vitae convallis est pharetra at. Cras condimentum nibh accumsan sapien finibus, eu porta mauris pharetra. Maecenas euismod vel orci iaculis pharetra. Aliquam ex mi, tristique vel mattis quis, efficitur ultrices lectus. Nam molestie ornare massa vitae rhoncus. In dolor arcu, faucibus quis tincidunt non, maximus ac quam. Integer ac lacinia purus. Nulla tristique, diam eget fringilla ornare, augue nunc dictum turpis, ut commodo tortor tortor ac arcu. Proin a diam tellus.
|
||||||
23
src/routes/integrations/another/+page.markdoc
Normal file
23
src/routes/integrations/another/+page.markdoc
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
layout: integration
|
||||||
|
title: Another
|
||||||
|
description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
date: 2023-05-02
|
||||||
|
featured: false
|
||||||
|
isPartner: true
|
||||||
|
isNew: true
|
||||||
|
cover: /images/blog/ai-announcement.png
|
||||||
|
category: AI
|
||||||
|
author:
|
||||||
|
avatar: '/images/community/avatars/souvik.png'
|
||||||
|
name: Souvik Sarkar
|
||||||
|
username: 'Jeet_2003'
|
||||||
|
platform:
|
||||||
|
- 'Self-hosted'
|
||||||
|
images:
|
||||||
|
- /images/blog/ai-announcement.png
|
||||||
|
- /images/blog/ai-announcement.png
|
||||||
|
- /images/blog/ai-announcement.png
|
||||||
|
---
|
||||||
|
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tristique aliquam sagittis. Nam luctus tincidunt risus, vitae convallis est pharetra at. Cras condimentum nibh accumsan sapien finibus, eu porta mauris pharetra. Maecenas euismod vel orci iaculis pharetra. Aliquam ex mi, tristique vel mattis quis, efficitur ultrices lectus. Nam molestie ornare massa vitae rhoncus. In dolor arcu, faucibus quis tincidunt non, maximus ac quam. Integer ac lacinia purus. Nulla tristique, diam eget fringilla ornare, augue nunc dictum turpis, ut commodo tortor tortor ac arcu. Proin a diam tellus.
|
||||||
@@ -19,6 +19,7 @@ const config = {
|
|||||||
layouts: {
|
layouts: {
|
||||||
default: absolute('./src/markdoc/layouts/Article.svelte'),
|
default: absolute('./src/markdoc/layouts/Article.svelte'),
|
||||||
article: absolute('./src/markdoc/layouts/Article.svelte'),
|
article: absolute('./src/markdoc/layouts/Article.svelte'),
|
||||||
|
integration: absolute('./src/markdoc/layouts/Integration.svelte'),
|
||||||
tutorial: absolute('./src/markdoc/layouts/Tutorial.svelte'),
|
tutorial: absolute('./src/markdoc/layouts/Tutorial.svelte'),
|
||||||
post: absolute('./src/markdoc/layouts/Post.svelte'),
|
post: absolute('./src/markdoc/layouts/Post.svelte'),
|
||||||
author: absolute('./src/markdoc/layouts/Author.svelte'),
|
author: absolute('./src/markdoc/layouts/Author.svelte'),
|
||||||
|
|||||||
Reference in New Issue
Block a user