Revert "update components"

This reverts commit 4ab7bb2f8e.
This commit is contained in:
Jesse Winton
2024-08-23 12:20:13 -04:00
parent d41196dca0
commit 428cbb0c14
27 changed files with 1613 additions and 1573 deletions

View File

@@ -11,7 +11,7 @@
<span class="icon-cheveron-down" aria-hidden="true" /> <span class="icon-cheveron-down" aria-hidden="true" />
</div> </div>
</summary> </summary>
<div class="collapsible-content u-flex-vertical"> <div class="collapsible-content flex flex-col">
<slot /> <slot />
</div> </div>
</details> </details>

View File

@@ -1,4 +1,7 @@
<ul class="collapsible u-width-full-line" style="--p-toggle-border-color: var(--web-color-border);"> <ul
class="collapsible w-full"
style="--p-toggle-border-color: var(--web-color-border);"
>
<slot /> <slot />
</ul> </ul>

View File

@@ -1,7 +1,4 @@
import Root from "./Root.svelte"; import Root from './Root.svelte';
import Item from "./Item.svelte"; import Item from './Item.svelte';
export { export { Root as Accordion, Item as AccordionItem };
Root as Accordion,
Item as AccordionItem
}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Media from '$lib/UI/Media.svelte'; import Media from "$lib/UI/Media.svelte";
import { formatDate } from '$lib/utils/date'; import { formatDate } from "$lib/utils/date";
export let title: string; export let title: string;
export let cover: string; export let cover: string;
@@ -27,8 +27,15 @@
{title} {title}
</h4> </h4>
<div class="web-author"> <div class="web-author">
<div class="u-flex u-cross-center u-gap-8"> <div class="flex items-center gap-2">
<img class="web-author-image" loading="lazy" src={avatar} width="24" height="24" alt={author} /> <img
class="web-author-image"
loading="lazy"
src={avatar}
width="24"
height="24"
alt={author}
/>
<div class="web-author-info"> <div class="web-author-info">
<h4 class="web-sub-body-400 web-u-color-text-primary">{author}</h4> <h4 class="web-sub-body-400 web-u-color-text-primary">{author}</h4>
<ul class="web-metadata web-caption-400 web-is-not-mobile"> <ul class="web-metadata web-caption-400 web-is-not-mobile">

View File

@@ -1,14 +1,15 @@
<script lang="ts"> <script lang="ts">
let carousel: HTMLElement; let carousel: HTMLElement;
export let size: 'default' | 'medium' | 'big' = 'default'; export let size: "default" | "medium" | "big" = "default";
export let gap = 32; export let gap = 32;
let scroll = 0; let scroll = 0;
function calculateScrollAmount(prev = false) { function calculateScrollAmount(prev = false) {
const direction = prev ? -1 : 1; const direction = prev ? -1 : 1;
const carouselSize = carousel?.clientWidth; const carouselSize = carousel?.clientWidth;
const childSize = (carousel.childNodes[0] as HTMLUListElement)?.clientWidth + gap; const childSize =
(carousel.childNodes[0] as HTMLUListElement)?.clientWidth + gap;
scroll = scroll || carouselSize; scroll = scroll || carouselSize;
@@ -22,13 +23,13 @@
function next() { function next() {
carousel.scrollBy({ carousel.scrollBy({
left: calculateScrollAmount(), left: calculateScrollAmount(),
behavior: 'smooth' behavior: "smooth",
}); });
} }
function prev() { function prev() {
carousel.scrollBy({ carousel.scrollBy({
left: calculateScrollAmount(true), left: calculateScrollAmount(true),
behavior: 'smooth' behavior: "smooth",
}); });
} }
@@ -37,14 +38,16 @@
function handleScroll() { function handleScroll() {
isStart = carousel.scrollLeft <= 0; isStart = carousel.scrollLeft <= 0;
isEnd = Math.ceil(carousel.scrollLeft + carousel.offsetWidth) >= carousel.scrollWidth; isEnd =
Math.ceil(carousel.scrollLeft + carousel.offsetWidth) >=
carousel.scrollWidth;
} }
</script> </script>
<div> <div>
<div class="u-flex u-flex-wrap u-cross-center u-margin-block-start-8"> <div class="mt-2 flex flex-wrap items-center">
<slot name="header" /> <slot name="header" />
<div class="nav u-flex u-gap-12 u-cross-end u-margin-inline-start-auto"> <div class="nav ml-auto flex items-end gap-3">
<button <button
class="web-icon-button" class="web-icon-button"
aria-label="Move carousel backward" aria-label="Move carousel backward"
@@ -64,11 +67,14 @@
</div> </div>
</div> </div>
<div class="carousel-wrapper" data-state={isStart ? 'start' : isEnd ? 'end' : 'middle'}> <div
class="carousel-wrapper"
data-state={isStart ? "start" : isEnd ? "end" : "middle"}
>
<ul <ul
class="web-grid-articles u-margin-block-start-32 carousel" class="web-grid-articles carousel mt-8"
class:is-medium={size === 'medium'} class:is-medium={size === "medium"}
class:is-big={size === 'big'} class:is-big={size === "big"}
style:gap="{gap}px" style:gap="{gap}px"
bind:this={carousel} bind:this={carousel}
on:scroll={handleScroll} on:scroll={handleScroll}
@@ -91,7 +97,7 @@
&::before, &::before,
&::after { &::after {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
width: 60px; width: 60px;
@@ -109,7 +115,7 @@
); );
} }
&[data-state='start']::before { &[data-state="start"]::before {
opacity: 0; opacity: 0;
} }
@@ -122,7 +128,7 @@
); );
} }
&[data-state='end']::after { &[data-state="end"]::after {
opacity: 0; opacity: 0;
} }
} }

View File

@@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from "$app/stores";
export let date: string | undefined = undefined; export let date: string | undefined = undefined;
let showFeedback = false; let showFeedback = false;
let feedbackType = ''; let feedbackType = "";
let email = ''; let email = "";
let comment = ''; let comment = "";
let error: string | undefined; let error: string | undefined;
let submitted = false; let submitted = false;
let submitting = false; let submitting = false;
@@ -13,30 +13,34 @@
async function handleSubmit() { async function handleSubmit() {
submitting = true; submitting = true;
error = undefined; error = undefined;
const response = await fetch('https://growth.appwrite.io/v1/feedback/docs', { const response = await fetch(
method: 'POST', "https://growth.appwrite.io/v1/feedback/docs",
{
method: "POST",
headers: { headers: {
'Content-Type': 'application/json' "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
email, email,
type: feedbackType, type: feedbackType,
route: $page.route.id, route: $page.route.id,
comment comment,
}) }),
}); },
);
submitting = false; submitting = false;
if (response.status >= 400) { if (response.status >= 400) {
error = response.status >= 500 ? 'Server Error.' : 'Error submitting form.'; error =
response.status >= 500 ? "Server Error." : "Error submitting form.";
return; return;
} }
comment = email = ''; comment = email = "";
submitted = true; submitted = true;
} }
function reset() { function reset() {
comment = email = ''; comment = email = "";
feedbackType = ''; feedbackType = "";
submitted = false; submitted = false;
error = undefined; error = undefined;
} }
@@ -47,20 +51,22 @@
</script> </script>
<section class="web-content-footer"> <section class="web-content-footer">
<header class="web-content-footer-header u-width-full-line"> <header class="web-content-footer-header w-full">
<div <div
class="u-flex u-gap-32 u-main-space-between u-cross-center u-width-full-line" class="flex w-full items-center justify-between gap-8"
style="flex-wrap: wrap-reverse;" style="flex-wrap: wrap-reverse;"
> >
<div class="u-flex u-gap-16 u-cross-center"> <div class="flex items-center gap-4">
<h5 class="web-main-body-600 web-u-color-text-primary">Was this page helpful?</h5> <h5 class="web-main-body-600 web-u-color-text-primary">
<div class="u-flex u-gap-8"> Was this page helpful?
</h5>
<div class="flex gap-2">
<button <button
class="web-radio-button" class="web-radio-button"
aria-label="helpful" aria-label="helpful"
on:click={() => { on:click={() => {
showFeedback = feedbackType === 'positive' ? false : true; showFeedback = feedbackType === "positive" ? false : true;
feedbackType = 'positive'; feedbackType = "positive";
}} }}
> >
<span class="icon-thumb-up" /> <span class="icon-thumb-up" />
@@ -69,8 +75,8 @@
class="web-radio-button" class="web-radio-button"
aria-label="unhelpful" aria-label="unhelpful"
on:click={() => { on:click={() => {
showFeedback = feedbackType === 'negative' ? false : true; showFeedback = feedbackType === "negative" ? false : true;
feedbackType = 'negative'; feedbackType = "negative";
}} }}
> >
<!-- TODO: fix the icon name on pink --> <!-- TODO: fix the icon name on pink -->
@@ -88,9 +94,9 @@
href="https://github.com/appwrite/website" href="https://github.com/appwrite/website"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="web-link u-flex u-gap-4 u-cross-baseline" class="web-link flex items-baseline gap-1"
> >
<span class="icon-pencil-alt u-contents" aria-hidden="true" /> <span class="icon-pencil-alt contents" aria-hidden="true" />
<span>Update on GitHub</span> <span>Update on GitHub</span>
</a> </a>
</li> </li>
@@ -104,10 +110,11 @@
class="web-card is-normal" class="web-card is-normal"
style="--card-padding:1rem" style="--card-padding:1rem"
> >
<div class="u-flex-vertical u-gap-8"> <div class="flex flex-col gap-2">
<label for="message"> <label for="message">
<span class="web-u-color-text-primary"> <span class="web-u-color-text-primary">
What did you {feedbackType === 'negative' ? 'dislike' : 'like'}? (optional) What did you {feedbackType === "negative" ? "dislike" : "like"}?
(optional)
</span> </span>
</label> </label>
<textarea <textarea
@@ -116,7 +123,7 @@
placeholder="Write your message" placeholder="Write your message"
bind:value={comment} bind:value={comment}
/> />
<label for="message" class="u-margin-block-start-8"> <label for="message" class="mt-2">
<span class="web-u-color-text-primary">Email</span> <span class="web-u-color-text-primary">Email</span>
</label> </label>
<input <input
@@ -129,21 +136,28 @@
/> />
</div> </div>
{#if submitted} {#if submitted}
<p class="web-u-color-text-primary u-margin-block-start-16"> <p class="web-u-color-text-primary mt-4">
Your message has been sent successfully. We appreciate your feedback. Your message has been sent successfully. We appreciate your feedback.
</p> </p>
{/if} {/if}
{#if error} {#if error}
<p class="web-u-color-text-primary u-margin-block-start-16"> <p class="web-u-color-text-primary mt-4">
There was an error submitting your feedback. Please try again later. There was an error submitting your feedback. Please try again later.
</p> </p>
{/if} {/if}
<div class="u-flex u-main-end u-margin-block-start-16 u-gap-8"> <div class="mt-4 flex justify-end gap-2">
<button class="web-button is-text" on:click={() => (showFeedback = false)}> <button
class="web-button is-text"
on:click={() => (showFeedback = false)}
>
<span>Cancel</span> <span>Cancel</span>
</button> </button>
<button type="submit" class="web-button" disabled={submitting || !email}> <button
type="submit"
class="web-button"
disabled={submitting || !email}
>
<span>Submit</span> <span>Submit</span>
</button> </button>
</div> </div>

View File

@@ -23,7 +23,7 @@
export let images: Array<string>; export let images: Array<string>;
</script> </script>
<div class="u-position-absolute web-u-hide-mobile root"> <div class="absolute web-u-hide-mobile root">
{#each headPositions as [size, top, left], i} {#each headPositions as [size, top, left], i}
{@const image = clamp(0, images.length - 1, i % images.length)} {@const image = clamp(0, images.length - 1, i % images.length)}
<FloatingHead <FloatingHead
@@ -33,7 +33,7 @@
{size} {size}
/> />
<div style:margin-block-end="0" style:padding="10%"> <div style:margin-block-end="0" style:padding="10%">
<img style:border-radius="50%" class="u-block" alt="" /> <img style:border-radius="50%" class="block" alt="" />
</div> </div>
{/each} {/each}
</div> </div>

View File

@@ -37,7 +37,7 @@
{ label: 'Functions', href: '/docs/products/functions' }, { label: 'Functions', href: '/docs/products/functions' },
{ label: 'Messaging', href: '/products/messaging' }, { label: 'Messaging', href: '/products/messaging' },
{ label: 'Storage', href: '/docs/products/storage' }, { label: 'Storage', href: '/docs/products/storage' },
{ label: 'Realtime', href: '/docs/apis/realtime' }, { label: 'Realtime', href: '/docs/apis/realtime' }
], ],
Learn: [ Learn: [
{ label: 'Docs', href: '/docs' }, { label: 'Docs', href: '/docs' },
@@ -93,7 +93,7 @@
<nav <nav
aria-label="Footer" aria-label="Footer"
class="web-footer-nav u-margin-block-start-100 u-position-relative" class="web-footer-nav relative mt-24"
class:web-u-sep-block-start={!noBorder} class:web-u-sep-block-start={!noBorder}
> >
<img class="web-logo" src="/images/logos/appwrite.svg" alt="appwrite" height="24" width="130" /> <img class="web-logo" src="/images/logos/appwrite.svg" alt="appwrite" height="24" width="130" />

View File

@@ -1,13 +1,18 @@
<script lang="ts"> <script lang="ts">
import { PUBLIC_APPWRITE_DASHBOARD } from '$env/static/public'; import Button from "./ui/Button.svelte";
import { PUBLIC_APPWRITE_DASHBOARD } from "$env/static/public";
export let classes = ''; export let classes = "";
</script> </script>
<a href={PUBLIC_APPWRITE_DASHBOARD} class={`web-button ${classes}`}> <Button class={classes} href={PUBLIC_APPWRITE_DASHBOARD}>
<span class="logged-in"><slot name="isLoggedIn">Go to Console</slot></span> <span class="hidden [data-logged-in]:block"
<span class="not-logged-in"><slot name="isNotLoggedIn">Get started</slot></span> ><slot name="isLoggedIn">Go to Console</slot></span
</a> >
<span class="btton [data-logged-in]:hidden"
><slot name="isNotLoggedIn">Get started</slot></span
>
</Button>
<style lang="scss"> <style lang="scss">
:global(body[data-logged-in]) { :global(body[data-logged-in]) {

View File

@@ -0,0 +1,98 @@
<script lang="ts">
export let title =
"Trusted by developers from the world's leading organizations";
const logos = [
{
src: "/images/logos/trusted-by/apple.svg",
alt: "Apple",
width: 42,
height: 48,
},
{
src: "/images/logos/trusted-by/oracle.svg",
alt: "ORACLE",
width: 136,
height: 17,
},
{
src: "/images/logos/trusted-by/tiktok.svg",
alt: "TikTok",
width: 133,
height: 32,
},
{
src: "/images/logos/trusted-by/intel.svg",
alt: "intel",
width: 76,
height: 30,
},
{
src: "/images/logos/trusted-by/ibm.svg",
alt: "IBM",
width: 74,
height: 30,
},
{
src: "/images/logos/trusted-by/american-airlines.svg",
alt: "American Airlines",
width: 147,
height: 24,
},
{
src: "/images/logos/trusted-by/deloitte.svg",
alt: "Deloitte.",
width: 103,
height: 20,
},
{
src: "/images/logos/trusted-by/gm.svg",
alt: "GM",
width: 48,
height: 48,
},
{
src: "/images/logos/trusted-by/ey.svg",
alt: "EY",
width: 46,
height: 48,
},
{
src: "/images/logos/trusted-by/nestle.svg",
alt: "Nestle",
width: 119,
height: 34,
},
{
src: "/images/logos/trusted-by/bosch.svg",
alt: "BOSCH",
width: 110,
height: 37,
},
{
src: "/images/logos/trusted-by/decathlon.svg",
alt: "DECATHLON",
width: 127,
height: 32,
},
];
</script>
<div class="my-32">
<div class="container">
<h2
class="font-aeonik-pro text-greyscale-100 mx-auto max-w-xl text-center text-4xl leading-10"
>
{title}
</h2>
<ul
class="web-u-padding-block-start-80 grid grid-cols-3 text-center md:grid-cols-6 md:gap-10"
>
{#each logos as { src, alt, width, height }, i}
<li class="grid place-content-center">
<img {src} {alt} {width} {height} />
</li>
{/each}
</ul>
</div>
</div>

View File

@@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
import { socials } from '$lib/constants'; import { socials } from "$lib/constants";
import ThemeSelect from './ThemeSelect.svelte'; import ThemeSelect from "./ThemeSelect.svelte";
export let variant: 'homepage' | 'docs' = 'homepage'; export let variant: "homepage" | "docs" = "homepage";
const year = new Date().getFullYear(); const year = new Date().getFullYear();
</script> </script>
{#if variant === 'homepage'} {#if variant === "homepage"}
<footer class="web-main-footer u-position-relative u-margin-block-start-48"> <footer class="web-main-footer relative mt-12">
<ul class="u-flex u-gap-8"> <ul class="flex gap-2">
{#each socials as social} {#each socials as social}
<li> <li>
<a <a
@@ -39,19 +39,17 @@
style:margin-top="-4px" style:margin-top="-4px"
/> />
<ul class="u-flex u-gap-16"> <ul class="flex gap-4">
<li><a class="web-link" href="/terms">Terms</a></li> <li><a class="web-link" href="/terms">Terms</a></li>
<li><a class="web-link" href="/privacy">Privacy</a></li> <li><a class="web-link" href="/privacy">Privacy</a></li>
<li><a class="web-link" href="/cookies">Cookies</a></li> <li><a class="web-link" href="/cookies">Cookies</a></li>
</ul> </ul>
</div> </div>
</footer> </footer>
{:else if variant === 'docs'} {:else if variant === "docs"}
<footer <footer class="web-main-footer is-with-bg-color relative mt-8 text-sm">
class="web-main-footer is-with-bg-color u-margin-block-start-32 u-small u-position-relative"
>
<div class="web-main-footer-grid-1"> <div class="web-main-footer-grid-1">
<ul class="web-main-footer-grid-1-column-1 u-flex u-gap-8"> <ul class="web-main-footer-grid-1-column-1 flex gap-2">
{#each socials as social} {#each socials as social}
<li> <li>
<a <a
@@ -69,13 +67,19 @@
<div class="web-main-footer-grid-1-column-2"> <div class="web-main-footer-grid-1-column-2">
<ThemeSelect /> <ThemeSelect />
</div> </div>
<ul class="web-main-footer-grid-1-column-3 u-cross-center web-main-footer-links"> <ul
class="web-main-footer-grid-1-column-3 web-main-footer-links items-center"
>
<li> <li>
<a href="/discord" target="_blank" rel="noopener noreferrer">Support</a> <a href="/discord" target="_blank" rel="noopener noreferrer"
>Support</a
>
</li> </li>
<li> <li>
<a href="https://appwrite.online" target="_blank" rel="noopener noreferrer" <a
>Status</a href="https://appwrite.online"
target="_blank"
rel="noopener noreferrer">Status</a
> >
</li> </li>
<!-- <li> <!-- <li>
@@ -90,7 +94,7 @@
{/if} {/if}
<style lang="scss"> <style lang="scss">
@use '$scss/abstract/variables/devices'; @use "$scss/abstract/variables/devices";
.web-icon-button { .web-icon-button {
display: grid; display: grid;
} }

View File

@@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
import { afterNavigate } from '$app/navigation'; import { afterNavigate } from "$app/navigation";
import { PUBLIC_APPWRITE_DASHBOARD } from '$env/static/public'; import { IsLoggedIn } from "$lib/components";
import { IsLoggedIn } from '$lib/components'; import { GITHUB_STARS } from "$lib/constants";
import { GITHUB_STARS } from '$lib/constants'; import type { NavLink } from "$lib/layouts/Main.svelte";
import type { NavLink } from '$lib/layouts/Main.svelte';
export let open = false; export let open = false;
export let links: NavLink[]; export let links: NavLink[];
@@ -15,16 +14,16 @@
<svelte:window on:resize={() => open && (open = false)} /> <svelte:window on:resize={() => open && (open = false)} />
<nav class="web-side-nav web-is-not-desktop" class:u-hide={!open}> <nav class="web-side-nav web-is-not-desktop" class:hidden={!open}>
<div class="web-side-nav-wrapper web-u-padding-inline-16"> <div class="web-side-nav-wrapper ps-4 pe-4">
<div class="u-flex items-center u-gap-8"> <div class="flex items-center gap-2">
<a <a
href={`${PUBLIC_APPWRITE_DASHBOARD}/register`} href="https://cloud.appwrite.io/register"
class="web-button is-secondary web-u-flex-1" class="web-button is-secondary flex-1"
> >
Sign up Sign up
</a> </a>
<IsLoggedIn classes="web-u-flex-1" /> <IsLoggedIn classes="flex-1" />
</div> </div>
<div class="web-side-nav-scroll"> <div class="web-side-nav-scroll">
<section> <section>

View File

@@ -1,22 +1,25 @@
<script context="module" lang="ts"> <script context="module" lang="ts">
export async function newsletter(name: string, email: string) { export async function newsletter(name: string, email: string) {
const response = await fetch('https://growth.appwrite.io/v1/newsletter/subscribe', { const response = await fetch(
method: 'POST', "https://growth.appwrite.io/v1/newsletter/subscribe",
{
method: "POST",
headers: { headers: {
'Content-Type': 'application/json' "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
name, name,
email email,
}) }),
}); },
);
return response; return response;
} }
</script> </script>
<script lang="ts"> <script lang="ts">
let email = ''; let email = "";
let name = ''; let name = "";
let submitted = false; let submitted = false;
let error: string | undefined; let error: string | undefined;
let submitting = false; let submitting = false;
@@ -27,7 +30,8 @@
const response = await newsletter(name, email); const response = await newsletter(name, email);
submitting = false; submitting = false;
if (response.status >= 400) { if (response.status >= 400) {
error = response.status >= 500 ? 'Server Error.' : 'Error submitting form.'; error =
response.status >= 500 ? "Server Error." : "Error submitting form.";
return; return;
} }
submitted = true; submitted = true;
@@ -60,8 +64,16 @@
color-interpolation-filters="sRGB" color-interpolation-filters="sRGB"
> >
<feFlood flood-opacity="0" result="BackgroundImageFix" /> <feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" /> <feBlend
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_1577_37321" /> mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_1577_37321"
/>
</filter> </filter>
<radialGradient <radialGradient
id="paint0_radial_1577_37321" id="paint0_radial_1577_37321"
@@ -81,22 +93,27 @@
<div class="web-big-padding-section"> <div class="web-big-padding-section">
<div class="web-big-padding-section-level-1"> <div class="web-big-padding-section-level-1">
<div class="web-big-padding-section-level-2"> <div class="web-big-padding-section-level-2">
<div class="web-container"> <div class="container">
<div class="web-grid-1-1-opt-2 u-gap-32"> <div class="grid-cols-2-opt-2 grid gap-8">
<div class=""> <div class="">
<div class="web-u-max-inline-size-none-mobile" class:web-u-max-width-380={!submitted}> <div
<section class="u-flex-vertical web-u-gap-20"> class="web-u-max-inline-size-none-mobile"
<h1 class="web-title web-u-color-text-primary">Subscribe to our newsletter</h1> class:web-max-w-[380px]={!submitted}
>
<section class="web-gap-5 flex flex-col">
<h1 class="web-title web-u-color-text-primary">
Subscribe to our newsletter
</h1>
<p class="web-description web-u-padding-block-end-40"> <p class="web-description web-u-padding-block-end-40">
Sign up to our company blog and get the latest insights from Appwrite. Learn more Sign up to our company blog and get the latest insights from
about engineering, product design, building community, and tips & tricks for using Appwrite. Learn more about engineering, product design,
Appwrite. building community, and tips & tricks for using Appwrite.
</p> </p>
</section> </section>
</div> </div>
</div> </div>
{#if submitted} {#if submitted}
<div class="u-flex u-gap-8 u-cross-center"> <div class="flex items-center gap-2">
<svg <svg
width="18" width="18"
height="18" height="18"
@@ -130,8 +147,12 @@
</span> </span>
</div> </div>
{:else} {:else}
<form method="post" on:submit|preventDefault={submit} class="u-flex-vertical u-gap-16"> <form
<div class="u-flex u-flex-vertical u-gap-4"> method="post"
on:submit|preventDefault={submit}
class="flex flex-col gap-4"
>
<div class="flex flex-col gap-1">
<label for="name">Your name</label> <label for="name">Your name</label>
<input <input
class="web-input-text" class="web-input-text"
@@ -143,7 +164,7 @@
bind:value={name} bind:value={name}
/> />
</div> </div>
<div class="u-flex u-flex-vertical u-gap-4"> <div class="flex flex-col gap-1">
<label for="email">Your email</label> <label for="email">Your email</label>
<input <input
class="web-input-text" class="web-input-text"
@@ -155,9 +176,13 @@
bind:value={email} bind:value={email}
/> />
</div> </div>
<button type="submit" class="web-button" disabled={submitting}>Sign up</button> <button type="submit" class="web-button" disabled={submitting}
>Sign up</button
>
{#if error} {#if error}
<span class="text"> Something went wrong. Please try again later. </span> <span class="text">
Something went wrong. Please try again later.
</span>
{/if} {/if}
</form> </form>
{/if} {/if}

View File

@@ -1,23 +1,28 @@
<script lang="ts"> <script lang="ts">
import { PUBLIC_APPWRITE_DASHBOARD } from '$env/static/public'; import { PUBLIC_APPWRITE_DASHBOARD } from "$env/static/public";
</script> </script>
<img src="/images/bgs/pre-footer.png" alt="" class="web-pre-footer-bg" style="z-index:-1" /> <img
src="/images/bgs/pre-footer.png"
alt=""
class="web-pre-footer-bg"
style="z-index:-1"
/>
<div class="web-grid-1-1 u-gap-32 web-u-row-gap-80 u-position-relative"> <div class="web-u-row-gap-80 relative grid grid-cols-2 gap-8">
<section class="web-hero u-flex web-u-row-gap-32 u-main-center u-cross-center"> <section class="web-hero web-u-row-gap-32 flex items-center justify-center">
<h2 class="web-display u-max-width-500 web-u-text-align-center web-u-color-text-primary"> <h2 class="web-display web-u-color-text-primary max-w-[500px] text-center">
Start building today Start building today
</h2> </h2>
<a <a
href={PUBLIC_APPWRITE_DASHBOARD} href={PUBLIC_APPWRITE_DASHBOARD}
class="web-button is-transparent web-u-cross-child-center" class="web-button is-transparent web-self-center"
> >
<span class="text">Get started</span> <span class="text">Get started</span>
</a> </a>
</section> </section>
<section <section
class="web-card is-transparent has-border-gradient web-u-max-inline-width-584-mobile web-u-margin-inline-auto-mobile web-u-inline-width-100-percent-mobile" class="web-card is-transparent has-border-gradient web-u-max-inline-width-584-mobile web-mx-auto-mobile web-u-inline-width-100-percent-mobile"
> >
<header class="web-strip-plans-header"> <header class="web-strip-plans-header">
<div class="web-strip-plans-header-wrapper web-u-row-gap-24"> <div class="web-strip-plans-header-wrapper web-u-row-gap-24">
@@ -73,7 +78,8 @@
<div class="info web-caption-500">per org/month</div> <div class="info web-caption-500">per org/month</div>
</div> </div>
<p class="web-strip-plans-info web-caption-500"> <p class="web-strip-plans-info web-caption-500">
For pro developers and production projects that need the ability to scale. For pro developers and production projects that need the ability to
scale.
</p> </p>
<button <button
class="web-button is-full-width-mobile is-secondary web-u-cross-child-end" class="web-button is-full-width-mobile is-secondary web-u-cross-child-end"

View File

@@ -1,145 +0,0 @@
<div class="web-big-padding-section-level-2">
<div class="web-container">
<h3 class="web-label web-u-color-text-primary u-text-center">
Keep exploring similar integrations
</h3>
<ul class="u-margin-block-start-32 l-grid-1">
<li>
<a
href="/docs/products/auth"
class="web-card product-card is-transparent u-block u-height-100-percent"
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
>
<div class="u-flex u-cross-center u-gap-8">
<img
src="/images/icons/illustrated/dark/auth.png"
alt=""
class=""
width="32"
height="32"
/>
<h4 class="web-main-body-400 web-u-color-text-primary">Auth</h4>
<span class="icon-arrow-right u-margin-inline-start-auto" aria-hidden="true"
></span>
</div>
<p class="web-sub-body-400 u-margin-block-start-4">
Build secure authentication and manage your users.
</p>
</a>
</li>
<li>
<a
href="/docs/products/functions"
class="web-card product-card is-transparent u-block u-height-100-percent"
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
>
<div class="u-flex u-cross-center u-gap-8">
<img
src="/images/icons/illustrated/dark/functions.png"
alt=""
class=""
width="32"
height="32"
/>
<h4 class="web-main-body-400 web-u-color-text-primary">Functions</h4>
<span class="icon-arrow-right u-margin-inline-start-auto" aria-hidden="true"
></span>
</div>
<p class="web-sub-body-400 u-margin-block-start-4">
Scale big and unlock limitless potential with Appwrite functions.
</p>
</a>
</li>
<li>
<a
href="/docs/products/databases"
class="web-card product-card is-transparent u-block u-height-100-percent"
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
>
<div class="u-flex u-cross-center u-gap-8">
<img
src="/images/icons/illustrated/dark/databases.png"
alt=""
class=""
width="32"
height="32"
/>
<h4 class="web-main-body-400 web-u-color-text-primary">Databases</h4>
<span class="icon-arrow-right u-margin-inline-start-auto" aria-hidden="true"
></span>
</div>
<p class="web-sub-body-400 u-margin-block-start-4">
Store and query structured data, ensuring scalable storage.
</p>
</a>
</li>
<li>
<a
href="/docs/products/messaging"
class="web-card product-card is-transparent u-block u-height-100-percent"
style="--card-padding:1.5rem;--card-padding-mobile:1.5rem;"
>
<div class="u-flex u-cross-center u-gap-8">
<img
src="/images/icons/illustrated/dark/messaging.png"
alt=""
class=""
width="32"
height="32"
/>
<h4 class="web-main-body-400 web-u-color-text-primary">Messaging</h4>
<span class="icon-arrow-right u-margin-inline-start-auto" aria-hidden="true"
></span>
</div>
<p class="web-sub-body-400 u-margin-block-start-4">
Manage your files project, using convenient APIs and utilities.
</p>
</a>
</li>
<li>
<a
href="/docs/apis/realtime"
class="web-card product-card is-transparent u-block u-height-100-percent"
style="--card-padding:1.5rem; --card-padding-mobile:1.5rem;"
>
<div class="u-flex u-cross-center u-gap-8">
<img
src="/images/icons/illustrated/dark/realtime.png"
alt=""
class=""
width="32"
height="32"
/>
<h4 class="web-main-body-400 web-u-color-text-primary">Realtime</h4>
<span class="icon-arrow-right u-margin-inline-start-auto" aria-hidden="true"
></span>
</div>
<p class="web-sub-body-400 u-margin-block-start-4">
Utilize realtime information from all Appwrite services.
</p>
</a>
</li>
</ul>
</div>
</div>
<style lang="scss">
.l-grid-1 {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
@media (max-width: 600px) {
gap: 1.25rem;
}
.product-card {
transition: all 100ms ease-in-out;
box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0);
&:hover {
box-shadow: inset 0px 0px 0px 1px rgba(255, 255, 255, 0.12);
}
}
}
</style>

View File

@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { afterNavigate, goto } from '$app/navigation'; import { afterNavigate, goto } from "$app/navigation";
import { layoutState } from '$lib/layouts/Docs.svelte'; import { layoutState } from "$lib/layouts/Docs.svelte";
import { isMac } from '$lib/utils/platform'; import { isMac } from "$lib/utils/platform";
import { createCombobox, melt } from '@melt-ui/svelte'; import { createCombobox, melt } from "@melt-ui/svelte";
import { MeiliSearch, type Hit, type Hits } from 'meilisearch'; import { MeiliSearch, type Hit, type Hits } from "meilisearch";
import { tick } from 'svelte'; import { tick } from "svelte";
export let open = true; export let open = true;
@@ -14,10 +14,10 @@
let container: HTMLDivElement; let container: HTMLDivElement;
const client = new MeiliSearch({ const client = new MeiliSearch({
host: 'https://search.appwrite.org', host: "https://search.appwrite.org",
apiKey: 'd7e83e21c0daf2a471ef4c463c7872e55b91b0cd02e2d20e9c6f6f1c4cd09ed3' apiKey: "d7e83e21c0daf2a471ef4c463c7872e55b91b0cd02e2d20e9c6f6f1c4cd09ed3",
}); });
const index = client.index<Props>('website'); const index = client.index<Props>("website");
type Props = { type Props = {
url: string; url: string;
@@ -40,7 +40,7 @@
async function search(value: string) { async function search(value: string) {
return index.search(value, { return index.search(value, {
limit: 20 limit: 20,
}); });
} }
@@ -53,15 +53,17 @@
} }
} }
function handleExit(event: MouseEvent & { currentTarget: EventTarget & HTMLDivElement }) { function handleExit(
event: MouseEvent & { currentTarget: EventTarget & HTMLDivElement },
) {
if (event.target === container) { if (event.target === container) {
open = false; open = false;
value = ''; value = "";
} }
} }
function createHref(hit: Hit<Props>): string { function createHref(hit: Hit<Props>): string {
const anchor = hit.anchor === '#' ? '' : hit.anchor ?? ''; const anchor = hit.anchor === "#" ? "" : (hit.anchor ?? "");
const target = hit.url + anchor; const target = hit.url + anchor;
return target.toString(); return target.toString();
@@ -69,29 +71,29 @@
const recommended: Hits<Props> = [ const recommended: Hits<Props> = [
{ {
uid: 'recommended-references-account', uid: "recommended-references-account",
url: '/docs/references/cloud/client-web/databases', url: "/docs/references/cloud/client-web/databases",
h1: 'API reference', h1: "API reference",
h2: 'Databases' h2: "Databases",
}, },
{ {
uid: 'recommended-references-teans', uid: "recommended-references-teans",
url: '/docs/references/cloud/client-web/teams', url: "/docs/references/cloud/client-web/teams",
h1: 'API reference', h1: "API reference",
h2: 'Teams' h2: "Teams",
}, },
{ {
uid: 'recommended-references-databases', uid: "recommended-references-databases",
url: '/docs/references/cloud/client-web/databases', url: "/docs/references/cloud/client-web/databases",
h1: 'API reference', h1: "API reference",
h2: 'Databases' h2: "Databases",
}, },
{ {
uid: 'recommended-references-storage', uid: "recommended-references-storage",
url: '/docs/references/cloud/client-web/storage', url: "/docs/references/cloud/client-web/storage",
h1: 'API reference', h1: "API reference",
h2: 'Storage' h2: "Storage",
} },
]; ];
afterNavigate(() => { afterNavigate(() => {
@@ -102,13 +104,13 @@
let inputEl: HTMLInputElement; let inputEl: HTMLInputElement;
$: if (open && inputEl) { $: if (open && inputEl) {
inputEl.value = ''; inputEl.value = "";
inputEl?.focus(); inputEl?.focus();
} }
const { const {
elements: { input, menu, option }, elements: { input, menu, option },
states: { inputValue } states: { inputValue },
} = createCombobox<Props>({ } = createCombobox<Props>({
forceVisible: true, forceVisible: true,
preventScroll: false, preventScroll: false,
@@ -118,20 +120,23 @@
if (next) { if (next) {
goto(next.value.url); goto(next.value.url);
tick().then(() => { tick().then(() => {
inputValue.set(''); inputValue.set("");
}); });
inputValue.set(''); inputValue.set("");
} }
return undefined; return undefined;
} },
}); });
function handleKeypress(event: KeyboardEvent) { function handleKeypress(event: KeyboardEvent) {
const cmdPressed = isMac() ? event.metaKey : event.ctrlKey; const cmdPressed = isMac() ? event.metaKey : event.ctrlKey;
if (cmdPressed && event.key.toLowerCase() === 'k') { if (cmdPressed && event.key.toLowerCase() === "k") {
event.preventDefault(); event.preventDefault();
$layoutState.showSearch = true; $layoutState.showSearch = true;
} else if (event.key.toLowerCase() === 'escape' || event.key.toLowerCase() === 'esc') { } else if (
event.key.toLowerCase() === "escape" ||
event.key.toLowerCase() === "esc"
) {
event.preventDefault(); event.preventDefault();
$layoutState.showSearch = false; $layoutState.showSearch = false;
} }
@@ -147,7 +152,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="wrapper u-position-fixed u-padding-0 u-inset-0 u-flex u-main-center u-cross-center" class="wrapper fixed inset-0 flex items-center justify-center p-0"
data-visible={open ? true : undefined} data-visible={open ? true : undefined}
style:z-index="100" style:z-index="100"
style:background="hsl(var(--web-color-black) / 0.3)" style:background="hsl(var(--web-color-black) / 0.3)"
@@ -157,17 +162,17 @@
on:click={handleExit} on:click={handleExit}
> >
<div <div
class="web-input-text-search-wrapper web-u-max-width-680 web-u-margin-inline-20 u-width-full-line" class="web-input-text-search-wrapper web-max-w-[680px] web-u-margin-inline-20 w-full"
> >
<span <span
class="web-icon-search u-z-index-5" class="web-icon-search z-[5]"
aria-hidden="true" aria-hidden="true"
style="inset-block-start:0.9rem" style="inset-block-start:0.9rem"
/> />
<div id="searchbox" /> <div id="searchbox" />
<input <input
class="web-input-button -u-padding-block-0 u-position-relative u-z-index-1" class="web-input-button relative z-1"
type="text" type="text"
id="search" id="search"
bind:value bind:value
@@ -178,13 +183,13 @@
bind:this={inputEl} bind:this={inputEl}
data-hit="-1" data-hit="-1"
on:keydown={(e) => { on:keydown={(e) => {
if (e.key === 'Tab') { if (e.key === "Tab") {
e.preventDefault(); e.preventDefault();
} }
}} }}
/> />
<div <div
class="web-card is-normal u-flex-vertical u-gap-24" class="web-card is-normal flex flex-col gap-6"
use:melt={$menu} use:melt={$menu}
style="--card-padding-mobile:1rem; border-radius:0 0 0.5rem 0.5rem;" style="--card-padding-mobile:1rem; border-radius:0 0 0.5rem 0.5rem;"
> >
@@ -192,18 +197,18 @@
<section> <section>
{#if results.length > 0} {#if results.length > 0}
<h6 class="web-eyebrow">{results.length} results found</h6> <h6 class="web-eyebrow">{results.length} results found</h6>
<ul class="u-flex-vertical u-gap-4 u-margin-block-start-8"> <ul class="mt-2 flex flex-col gap-1">
{#each results as hit, i (hit.uid)} {#each results as hit, i (hit.uid)}
{@const relevantSubtitle = getRelevantSubtitle(hit)} {@const relevantSubtitle = getRelevantSubtitle(hit)}
<li> <li>
<a <a
data-hit={i} data-hit={i}
href={createHref(hit)} href={createHref(hit)}
class="web-button web-caption-400 is-text u-flex-vertical u-gap-8 u-min-width-100-percent class="web-button web-caption-400 is-text web-u-padding-block-8 web-padding-inline-12 web-u-cross-start flex
web-u-padding-block-8 web-padding-inline-12 web-u-cross-start u-max-width-100-percent" max-w-full min-w-full flex-col gap-2"
use:melt={$option({ use:melt={$option({
value: hit, value: hit,
label: hit.title ?? i.toString() label: hit.title ?? i.toString(),
})} })}
> >
<div class="web-u-trim-1"> <div class="web-u-trim-1">
@@ -216,9 +221,7 @@
{/if} {/if}
</div> </div>
{#if hit.p} {#if hit.p}
<div <div class="web-u-color-text-secondary web-u-trim-1">
class="web-u-color-text-secondary web-u-trim-1"
>
{hit.p} {hit.p}
</div> </div>
{/if} {/if}
@@ -228,14 +231,14 @@
</ul> </ul>
{:else} {:else}
<p class="web-caption-400"> <p class="web-caption-400">
No results found for <span class="u-bold">{value}</span> No results found for <span class="font-bold">{value}</span>
</p> </p>
{/if} {/if}
</section> </section>
{/if} {/if}
<section> <section>
<h6 class="web-eyebrow">Recommended</h6> <h6 class="web-eyebrow">Recommended</h6>
<ul class="u-flex-vertical u-gap-4 u-margin-block-start-8"> <ul class="mt-2 flex flex-col gap-1">
{#each recommended as hit, i (hit.uid)} {#each recommended as hit, i (hit.uid)}
{@const index = i + (results.length ? results.length : 0)} {@const index = i + (results.length ? results.length : 0)}
<li> <li>
@@ -244,9 +247,9 @@
href={createHref(hit)} href={createHref(hit)}
use:melt={$option({ use:melt={$option({
value: hit, value: hit,
label: hit.title ?? i.toString() label: hit.title ?? i.toString(),
})} })}
class="web-button web-caption-400 is-text u-flex-vertical u-gap-8 u-min-width-100-percent web-u-padding-block-4 web-u-cross-start" class="web-button web-caption-400 is-text web-u-padding-block-4 web-u-cross-start flex min-w-full flex-col gap-2"
> >
<div class="web-u-trim-1"> <div class="web-u-trim-1">
<span class="web-u-color-text-secondary">{hit.h1}</span> <span class="web-u-color-text-secondary">{hit.h1}</span>

View File

@@ -8,19 +8,22 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import { createSelect, melt, type CreateSelectProps } from '@melt-ui/svelte'; import { createSelect, melt, type CreateSelectProps } from "@melt-ui/svelte";
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from "svelte";
import { fly, type FlyParams } from 'svelte/transition'; import { fly, type FlyParams } from "svelte/transition";
export let options: Array<SelectOption>; export let options: Array<SelectOption>;
export let nativeMobile = false; export let nativeMobile = false;
export let value: unknown | undefined = undefined; export let value: unknown | undefined = undefined;
export let onSelectedChange: CreateSelectProps['onSelectedChange'] = undefined; export let onSelectedChange: CreateSelectProps["onSelectedChange"] =
undefined;
// TODO: This id currently gets overriden by Melt. We should either use the label el, or // TODO: This id currently gets overriden by Melt. We should either use the label el, or
// allow passing down ids in Melt. // allow passing down ids in Melt.
export let id: string | undefined = undefined; export let id: string | undefined = undefined;
export let preventScroll = false; export let preventScroll = false;
export let placement: NonNullable<CreateSelectProps['positioning']>['placement'] = 'bottom'; export let placement: NonNullable<
CreateSelectProps["positioning"]
>["placement"] = "bottom";
const dispatch = createEventDispatcher<{ const dispatch = createEventDispatcher<{
change: unknown; change: unknown;
@@ -28,12 +31,12 @@
const { const {
elements: { trigger, menu, option: optionEl, group: groupEl, groupLabel }, elements: { trigger, menu, option: optionEl, group: groupEl, groupLabel },
states: { open, selected, selectedLabel } states: { open, selected, selectedLabel },
} = createSelect<unknown>({ } = createSelect<unknown>({
preventScroll, preventScroll,
positioning: { positioning: {
sameWidth: true, sameWidth: true,
placement placement,
}, },
forceVisible: true, forceVisible: true,
onSelectedChange({ curr, next }) { onSelectedChange({ curr, next }) {
@@ -41,12 +44,12 @@
onSelectedChange({ curr, next }); onSelectedChange({ curr, next });
} }
value = next?.value; value = next?.value;
dispatch('change', next?.value); dispatch("change", next?.value);
return next; return next;
}, },
portal: null, portal: null,
scrollAlignment: 'center' scrollAlignment: "center",
}); });
$: selectedOption = options.find((o) => o.value === value); $: selectedOption = options.find((o) => o.value === value);
@@ -55,27 +58,33 @@
selected.set(selectedOption); selected.set(selectedOption);
} }
const DEFAULT_GROUP = 'default'; const DEFAULT_GROUP = "default";
type Group = { type Group = {
label: string; label: string;
options: SelectOption<unknown>[]; options: SelectOption<unknown>[];
}; };
$: groups = (function getGroups(): Group[] { $: groups = (function getGroups(): Group[] {
const groups = options.reduce<Record<string, SelectOption[]>>((carry, option) => { const groups = options.reduce<Record<string, SelectOption[]>>(
(carry, option) => {
const group = option.group ?? DEFAULT_GROUP; const group = option.group ?? DEFAULT_GROUP;
if (!carry[group]) { if (!carry[group]) {
carry[group] = []; carry[group] = [];
} }
carry[group].push(option); carry[group].push(option);
return carry; return carry;
}, {}); },
{},
);
return Object.entries(groups).map(([label, options]) => ({ label, options })); return Object.entries(groups).map(([label, options]) => ({
label,
options,
}));
})(); })();
$: flyParams = { $: flyParams = {
duration: 150, duration: 150,
y: placement === 'top' ? 4 : -4 y: placement === "top" ? 4 : -4,
} as FlyParams; } as FlyParams;
</script> </script>
@@ -106,7 +115,7 @@
{#each groups as group} {#each groups as group}
{@const isDefault = group.label === DEFAULT_GROUP} {@const isDefault = group.label === DEFAULT_GROUP}
{#if isDefault} {#if isDefault}
<div class="u-flex u-flex-vertical u-gap-2"> <div class="flex flex-col gap-0.5">
{#each group.options as option} {#each group.options as option}
<button class="web-select-option" use:melt={$optionEl(option)}> <button class="web-select-option" use:melt={$optionEl(option)}>
{#if option.icon} {#if option.icon}
@@ -118,7 +127,10 @@
</div> </div>
{:else} {:else}
<div class="web-select-group" use:melt={$groupEl(group.label)}> <div class="web-select-group" use:melt={$groupEl(group.label)}>
<span class="web-select-group-label" use:melt={$groupLabel(group.label)}> <span
class="web-select-group-label"
use:melt={$groupLabel(group.label)}
>
{group.label} {group.label}
</span> </span>
@@ -138,7 +150,7 @@
<div <div
class="web-select is-colored web-is-only-mobile web-u-inline-width-100-percent-mobile-break1" class="web-select is-colored web-is-only-mobile web-u-inline-width-100-percent-mobile-break1"
style:display={nativeMobile ? undefined : 'none'} style:display={nativeMobile ? undefined : "none"}
> >
{#if selectedOption?.icon} {#if selectedOption?.icon}
<span class={selectedOption.icon} aria-hidden="true" /> <span class={selectedOption.icon} aria-hidden="true" />

View File

@@ -1,16 +1,16 @@
<script lang="ts"> <script lang="ts">
import { createSwitch, melt } from '@melt-ui/svelte'; import { createSwitch, melt } from "@melt-ui/svelte";
export let checked = false; export let checked = false;
const { const {
elements: { root }, elements: { root },
states: { checked: meltChecked } states: { checked: meltChecked },
} = createSwitch({ } = createSwitch({
onCheckedChange({ next }) { onCheckedChange({ next }) {
checked = next; checked = next;
return next; return next;
} },
}); });
$: meltChecked.set(checked); $: meltChecked.set(checked);
@@ -41,7 +41,7 @@
transition: ease 150ms; transition: ease 150ms;
} }
.melt-switch :global([data-state='checked']) { .melt-switch :global([data-state="checked"]) {
background-color: #fd366e; background-color: #fd366e;
} }
@@ -61,7 +61,7 @@
transition: ease 150ms; transition: ease 150ms;
} }
:global(button[data-state='checked']) .thumb { :global(button[data-state="checked"]) .thumb {
--x: calc(var(--width) - var(--thumb-size) - var(--padding)); --x: calc(var(--width) - var(--thumb-size) - var(--padding));
transform: translateX(var(--x)) translateY(-50%); transform: translateX(var(--x)) translateY(-50%);
} }

View File

@@ -57,8 +57,7 @@
name: 'React Native', name: 'React Native',
href: '/docs/quick-starts/react-native', href: '/docs/quick-starts/react-native',
image: `/images/platforms/${$themeInUse}/react-native.svg` image: `/images/platforms/${$themeInUse}/react-native.svg`
}, }
] as Array<{ ] as Array<{
name: string; name: string;
href: string; href: string;
@@ -66,12 +65,17 @@
}>; }>;
</script> </script>
<ul class="u-flex u-flex-wrap u-gap-16 web-u-margin-block-32-mobile web-u-margin-block-40-not-mobile"> <ul class="flex flex-wrap gap-4 web-u-margin-block-32-mobile web-u-margin-block-40-not-mobile">
{#each platforms as platform} {#each platforms as platform}
<Tooltip> <Tooltip>
<li> <li>
<a href={platform.href} class="web-icon-button web-box-icon has-border-gradient"> <a href={platform.href} class="web-icon-button web-box-icon has-border-gradient">
<img src={platform.image} alt="{platform.name} quick start" width="32" height="32" /> <img
src={platform.image}
alt="{platform.name} quick start"
width="32"
height="32"
/>
</a> </a>
</li> </li>
<svelte:fragment slot="tooltip">{platform.name}</svelte:fragment> <svelte:fragment slot="tooltip">{platform.name}</svelte:fragment>

View File

@@ -16,7 +16,10 @@
<aside class="web-grid-120-1fr-auto-side" class:web-is-mobile-closed={!showToc}> <aside class="web-grid-120-1fr-auto-side" class:web-is-mobile-closed={!showToc}>
<div class="web-page-steps"> <div class="web-page-steps">
<div class="web-page-steps-location web-is-not-mobile" style="--location:{progress * 100}%;"> <div
class="web-page-steps-location web-is-not-mobile"
style="--location:{progress * 100}%;"
>
<span class="web-page-steps-location-button"> <span class="web-page-steps-location-button">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -1,168 +0,0 @@
<script lang="ts">
import embla from 'embla-carousel-svelte';
import {
type EmblaCarouselType,
type EmblaEventType,
type EmblaOptionsType,
type EmblaPluginType
} from 'embla-carousel';
import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures';
let emblaApi: EmblaCarouselType;
let options: EmblaOptionsType = {
align: 'center',
skipSnaps: true,
loop: true
};
let hasPrev: boolean = false;
let hasNext: boolean = true;
const togglePrevNextBtnsState = () => {
if (emblaApi.canScrollPrev()) hasPrev = true;
else hasPrev = false;
if (emblaApi.canScrollNext()) hasNext = true;
else hasNext = false;
};
let plugins: EmblaPluginType[] = [WheelGesturesPlugin()];
let selectedScrollIndex = 0;
const onSelect = (index: number) => {
emblaApi.scrollTo(index);
selectedScrollIndex = emblaApi.selectedScrollSnap();
};
const onPrev = () => {
emblaApi.scrollPrev();
selectedScrollIndex = emblaApi.selectedScrollSnap();
};
const onNext = () => {
emblaApi.scrollNext();
selectedScrollIndex = emblaApi.selectedScrollSnap();
};
const TWEEN_FACTOR_BASE = 0.52;
let tweenFactor = 0;
let tweenNodes: HTMLElement[] = [];
const numberWithinRange = (number: number, min: number, max: number) =>
Math.min(Math.max(number, min), max);
const setTweenNodes = (emblaApi: EmblaCarouselType): void => {
tweenNodes = emblaApi.slideNodes().map((slideNode) => {
return slideNode.querySelector('.embla__slide__number') as HTMLElement;
});
};
const setTweenFactor = (emblaApi: EmblaCarouselType): void => {
tweenFactor = TWEEN_FACTOR_BASE * emblaApi.scrollSnapList().length;
};
const tweenScale = (emblaApi: EmblaCarouselType, eventName?: EmblaEventType): void => {
const engine = emblaApi.internalEngine();
const scrollProgress = emblaApi.scrollProgress();
const slidesInView = emblaApi.slidesInView();
const isScrollEvent = eventName === 'scroll';
emblaApi.scrollSnapList().forEach((scrollSnap, snapIndex) => {
let diffToTarget = scrollSnap - scrollProgress;
const slidesInSnap = engine.slideRegistry[snapIndex];
slidesInSnap.forEach((slideIndex) => {
if (isScrollEvent && !slidesInView.includes(slideIndex)) return;
if (engine.options.loop) {
engine.slideLooper.loopPoints.forEach((loopItem) => {
const target = loopItem.target();
if (slideIndex === loopItem.index && target !== 0) {
const sign = Math.sign(target);
if (sign === -1) {
diffToTarget = scrollSnap - (1 + scrollProgress);
}
if (sign === 1) {
diffToTarget = scrollSnap + (1 - scrollProgress);
}
}
});
}
const tweenValue = 1 - Math.abs(diffToTarget * tweenFactor);
const scale = numberWithinRange(tweenValue, 0.8, 1).toString();
const tweenNode = tweenNodes[slideIndex];
tweenNode.style.transform = `scale(${scale})`;
});
});
};
const onEmblaInit = (event: CustomEvent<EmblaCarouselType>) => {
emblaApi = event.detail;
setTweenNodes(emblaApi);
setTweenFactor(emblaApi);
tweenScale(emblaApi);
emblaApi
.on('scroll', () => {
selectedScrollIndex = emblaApi.selectedScrollSnap();
})
.on('init', togglePrevNextBtnsState)
.on('select', togglePrevNextBtnsState)
.on('reInit', togglePrevNextBtnsState)
.on('reInit', setTweenNodes)
.on('reInit', setTweenFactor)
.on('reInit', tweenScale)
.on('scroll', tweenScale)
.on('slideFocus', tweenScale);
};
</script>
<div class="embla web-carousel">
{#if hasPrev}
<button class="web-carousel-button web-carousel-button-start" on:click={onPrev}>
<span class="web-icon-arrow-left" aria-hidden="true"></span>
</button>
{/if}
{#if hasNext}
<button class="web-carousel-button web-carousel-button-end" on:click={onNext}>
<span class="web-icon-arrow-right" aria-hidden="true"></span>
</button>
{/if}
<div class="embla__viewport" use:embla={{ options, plugins }} on:emblaInit={onEmblaInit}>
<ul class="embla__container">
<slot />
</ul>
</div>
</div>
<div class="web-carousel-bullets">
<ul class="web-carousel-bullets-list">
{#each Array.from({ length: emblaApi?.scrollSnapList().length }) as _, i}
<li class="web-carousel-bullets-item">
<button
class="web-carousel-bullets-button"
class:is-selected={selectedScrollIndex === i}
aria-label={`gallery item ${i + 1}`}
on:click={() => onSelect(i)}
></button>
</li>
{/each}
</ul>
</div>
<style>
.embla {
overflow: hidden;
position: relative;
}
.embla__container {
display: flex;
}
</style>

View File

@@ -1,25 +0,0 @@
<li class="slide web-carousel-item">
<div class="embla__slide__number">
<slot />
</div>
</li>
<style lang="scss">
@use '$scss/abstract' as *;
.slide {
cursor: grab;
&:active {
cursor: grabbing;
}
@media (max-width: 768px) {
flex: 0 0 100%;
}
flex: 0 0 50%;
min-width: 0;
margin-right: pxToRem(16);
}
</style>

View File

@@ -1,2 +0,0 @@
export { default as Root } from './Carousel.svelte';
export { default as Slide } from './CarouselSlide.svelte';

View File

@@ -1,16 +1,16 @@
export { default as FooterNav } from './FooterNav.svelte'; export { default as FooterNav } from "./FooterNav.svelte";
export { default as MainFooter } from './MainFooter.svelte'; export { default as MainFooter } from "./MainFooter.svelte";
export { default as PreFooter } from './PreFooter.svelte'; export { default as PreFooter } from "./PreFooter.svelte";
export { default as MobileNav } from './MobileNav.svelte'; export { default as MobileNav } from "./MobileNav.svelte";
export { default as Switch } from './Switch.svelte'; export { default as Switch } from "./Switch.svelte";
export { default as Newsletter } from './Newsletter.svelte'; export { default as Newsletter } from "./Newsletter.svelte";
export { default as Tooltip } from './Tooltip.svelte'; export { default as Tooltip } from "./Tooltip.svelte";
export { default as Article } from './Article.svelte'; export { default as Article } from "./Article.svelte";
export { default as Carousel } from './Carousel.svelte'; export { default as Carousel } from "./Carousel.svelte";
export { default as FloatingHeads } from './FloatingHeads.svelte'; export { default as FloatingHeads } from "./FloatingHeads.svelte";
export { default as FloatingHead } from './FloatingHead.svelte'; export { default as FloatingHead } from "./FloatingHead.svelte";
export { default as Feedback } from './Feedback.svelte'; export { default as Feedback } from "./Feedback.svelte";
export { default as Select } from './Select.svelte'; export { default as Select } from "./Select.svelte";
export { default as MetricCard } from './MetricCard.svelte'; export { default as MetricCard } from "./MetricCard.svelte";
export { default as IsLoggedIn } from './IsLoggedIn.svelte'; export { default as IsLoggedIn } from "./IsLoggedIn.svelte";
export { default as Search } from './Search.svelte'; export { default as Search } from "./Search.svelte";

View File

@@ -0,0 +1,97 @@
<script lang="ts">
import { classNames } from "$lib/utils/classnames";
import type {
HTMLButtonAttributes,
HTMLAnchorAttributes,
} from "svelte/elements";
import { cva, type VariantProps } from "cva";
const button = cva(
[
"flex w-fit min-h-10 items-center justify-center gap-2 rounded-lg px-4 text-white transition-all select-none",
],
{
variants: {
variant: {
primary: [
"to-secondary-100 bg-gradient-to-br from-pink-500 via-pink-500",
"hover:shadow-[0_0_2rem_#fd366e52]",
],
secondary: ["bg-[#fd366e0a] relative"],
text: [
"bg-transparent border-transparent",
"hover:bg-gradient-to-b from-[#ffffff0f] via-[#ffffff1a] to-[#ffffff0f];",
],
},
},
},
);
type ButtonProps =
| (HTMLButtonAttributes & { href?: undefined })
| (HTMLAnchorAttributes & { href: string });
type $$Props = ButtonProps & VariantProps<typeof button>;
export let href: $$Props["href"] = undefined;
export let variant: $$Props["variant"] = "primary";
const { class: classes, ...props } = $$restProps;
const buttonClasses = classNames(button({ variant }), classes);
</script>
{#if href}
<a
{...props}
{href}
class={buttonClasses}
class:has-border-gradient={variant === "secondary"}
>
<slot />
</a>
{:else}
<button
{...props}
class={buttonClasses}
class:has-border-gradient={variant === "secondary"}
>
<slot />
</button>
{/if}
<style>
.has-border-gradient {
--border-gradient-before: linear-gradient(
to bottom,
rgba(253, 54, 110, 0.48) 0%,
rgba(253, 54, 110, 0) 180%
);
--border-gradient-after: radial-gradient(
42.86% 42.86% at 50.55% -0%,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0) 100%
);
&::before {
background: var(--border-gradient-before) border-box;
}
&::after {
background: var(--border-gradient-after);
}
&::before,
&::after {
content: "";
position: absolute;
inset: 0;
border-radius: var(--radius-lg);
border: 1px solid transparent;
mask:
linear-gradient(#fff 0 0) padding-box,
linear-gradient(#fff 0 0);
mask-composite: exclude;
pointer-events: none;
}
}
</style>

View File

@@ -0,0 +1,97 @@
<script lang="ts">
import { classNames } from "$lib/utils/classnames";
import type {
HTMLButtonAttributes,
HTMLAnchorAttributes,
} from "svelte/elements";
import { cva, type VariantProps } from "cva";
const card = cva(
[
"flex w-fit min-h-10 items-center justify-center gap-2 rounded-2xl px-6 text-white transition-all select-none",
],
{
variants: {
variant: {
primary: [
"to-secondary-100 bg-gradient-to-br from-pink-500 via-pink-500",
"hover:shadow-[0_0_2rem_#fd366e52]",
],
secondary: ["bg-[#fd366e0a] relative"],
text: [
"bg-transparent border-transparent",
"hover:bg-gradient-to-b from-[#ffffff0f] via-[#ffffff1a] to-[#ffffff0f];",
],
},
},
},
);
type ButtonProps =
| (HTMLButtonAttributes & { href?: undefined })
| (HTMLAnchorAttributes & { href: string });
type $$Props = ButtonProps & VariantProps<typeof card>;
export let href: $$Props["href"] = undefined;
export let variant: $$Props["variant"] = "primary";
const { class: classes, ...props } = $$restProps;
const buttonClasses = classNames(card({ variant }), classes);
</script>
{#if href}
<a
{...props}
{href}
class={buttonClasses}
class:has-border-gradient={variant === "secondary"}
>
<slot />
</a>
{:else}
<button
{...props}
class={buttonClasses}
class:has-border-gradient={variant === "secondary"}
>
<slot />
</button>
{/if}
<style>
.has-border-gradient {
--border-gradient-before: linear-gradient(
to bottom,
rgba(253, 54, 110, 0.48) 0%,
rgba(253, 54, 110, 0) 180%
);
--border-gradient-after: radial-gradient(
42.86% 42.86% at 50.55% -0%,
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0) 100%
);
&::before {
background: var(--border-gradient-before) border-box;
}
&::after {
background: var(--border-gradient-after);
}
&::before,
&::after {
content: "";
position: absolute;
inset: 0;
border-radius: var(--radius-lg);
border: 1px solid transparent;
mask:
linear-gradient(#fff 0 0) padding-box,
linear-gradient(#fff 0 0);
mask-composite: exclude;
pointer-events: none;
}
}
</style>

View File