pretty selects everywhere

This commit is contained in:
tglide
2023-10-12 12:44:56 +01:00
parent 776bcc9bee
commit f2073ee532
7 changed files with 670 additions and 602 deletions

View File

@@ -28,7 +28,8 @@
},
title: 'Auth',
subtitle: 'Secure login for all users',
description: 'Authenticate users securely with multiple login methods like Email/Password, SMS, OAuth, Annoymous, Magic URLs and more.',
description:
'Authenticate users securely with multiple login methods like Email/Password, SMS, OAuth, Annoymous, Magic URLs and more.',
features: [
'30+ login methods',
'Support for teams, roles and user labels',
@@ -44,14 +45,13 @@
},
title: 'Databases',
subtitle: 'Store, query and manage data',
description:
'Scalable and robust database backed by your favorite technologies.',
description: 'Scalable and robust database backed by your favorite technologies.',
features: [
'Never paused',
'Fast in-memory caching',
'Advanced permission models',
'Custom data validation',
'Relationships support',
'Relationships support'
],
shot: './images/products/databases.png'
},
@@ -78,7 +78,8 @@
},
title: 'Storage',
subtitle: 'Upload and manage files',
description: 'Securely store files with advanced compression, encryption and image transformations.',
description:
'Securely store files with advanced compression, encryption and image transformations.',
features: [
'File encryption at rest and transit',
'Built-in image transformation capabilities',
@@ -97,7 +98,7 @@
features: [
'Unlimited subscriptions',
'Built-in permission management',
'Support for DBs, Auth, Storage & Functions',
'Support for DBs, Auth, Storage & Functions'
],
shot: './images/products/realtime.png'
}
@@ -123,6 +124,7 @@
import { Realtime, realtimeController } from './realtime';
import { postController } from './post';
import Post from './post/post.svelte';
import { anyify } from '$lib/utils/anyify';
/* Basic Animation setup */
let scrollInfo = {
@@ -180,8 +182,6 @@
}
});
})();
const anyify = (x: unknown) => x as any;
</script>
<div
@@ -202,9 +202,14 @@
in:fly={{ duration: 250, delay: 250, y: -300 }}
>
{#if scrollInfo.percentage > -0.1}
<span class="aw-badges aw-eyebrow" transition:slide={{ axis: 'x' }}>Products_</span>
<span class="aw-badges aw-eyebrow" transition:slide={{ axis: 'x' }}
>Products_</span
>
<h2 class="aw-display aw-u-color-text-primary " transition:fly={{ y: 16, delay: 250 }}>
<h2
class="aw-display aw-u-color-text-primary"
transition:fly={{ y: 16, delay: 250 }}
>
Your backend, minus the hassle
</h2>
<p
@@ -214,7 +219,8 @@
delay: 400
}}
>
Build secure and scalable applications with less code. Add authentication, databases, storage, and more using Appwrite's development platform.
Build secure and scalable applications with less code. Add authentication,
databases, storage, and more using Appwrite's development platform.
</p>
{/if}
</div>
@@ -226,7 +232,9 @@
data-active={scrollInfo.percentage > 0.075 ? '' : undefined}
>
<div class="text" id="pd-{$elId}">
<ScrollIndicator percentage={toScale(scrollInfo.percentage, animScale, [0, 1])} />
<ScrollIndicator
percentage={toScale(scrollInfo.percentage, animScale, [0, 1])}
/>
<ul class="descriptions">
{#each products as product}
{@const copy = infos[product]}
@@ -235,7 +243,12 @@
{#if copy}
<li data-active={isActive ? '' : undefined}>
<h3>
<img src={isActive ? copy.icon.active : copy.icon.inactive} alt="" width="32" height="32" />
<img
src={isActive ? copy.icon.active : copy.icon.inactive}
alt=""
width="32"
height="32"
/>
<span class="aw-label">{copy.title}</span>
</h3>
{#if isActive}

View File

@@ -9,37 +9,49 @@
<script lang="ts">
import { createSelect, melt, type CreateSelectProps } from '@melt-ui/svelte';
import { fly } from 'svelte/transition';
import { createEventDispatcher } from 'svelte';
import { fly, type FlyParams } from 'svelte/transition';
export let options: Array<SelectOption>;
export let nativeMobile = false;
export let selected: unknown | undefined = undefined;
export let value: unknown | undefined = undefined;
export let onSelectedChange: CreateSelectProps['onSelectedChange'] = undefined;
// TODO: This id currently gets overriden by Melt. We should either use the label el, or
// allow passing down ids in Melt.
export let id: string | undefined = undefined;
export let preventScroll = false;
export let placement: NonNullable<CreateSelectProps['positioning']>['placement'] = 'bottom';
const dispatch = createEventDispatcher<{
change: unknown;
}>();
const {
elements: { trigger, menu, option: optionEl, group: groupEl, groupLabel },
states: { open, selected: localSelected, selectedLabel }
states: { open, selected, selectedLabel }
} = createSelect<unknown>({
preventScroll: false,
preventScroll,
positioning: {
sameWidth: true,
fitViewport: true
fitViewport: true,
placement
},
forceVisible: true,
onSelectedChange({ curr, next }) {
if (onSelectedChange) {
onSelectedChange({ curr, next });
}
selected = next?.value;
value = next?.value;
dispatch('change', next?.value);
return next;
}
});
$: selectedOption = options.find((o) => o.value === selected);
$: selectedOption = options.find((o) => o.value === value);
$: if (selectedOption) {
localSelected.set(selectedOption);
selected.set(selectedOption);
}
const DEFAULT_GROUP = 'default';
@@ -59,10 +71,16 @@
return Object.entries(groups).map(([label, options]) => ({ label, options }));
})();
$: flyParams = {
duration: 150,
y: placement === 'top' ? 4 : -4
} as FlyParams;
</script>
<button
class="aw-select is-colored"
{id}
class:aw-is-not-mobile={nativeMobile}
use:melt={$trigger}
aria-label="Select theme"
@@ -82,14 +100,25 @@
class:aw-is-not-mobile={nativeMobile}
style:z-index={10000}
use:melt={$menu}
transition:fly={{ y: 4, duration: 150 }}
transition:fly={flyParams}
>
{#each groups as group}
{@const isDefault = group.label === DEFAULT_GROUP}
<div class="aw-select-group" use:melt={$groupEl(group.label)}>
{#if !isDefault}
<span class="aw-select-group-label" use:melt={$groupLabel}>{group.label}</span>
{#if isDefault}
{#each options as option}
<button class="aw-select-option" use:melt={$optionEl(option)}>
{#if option.icon}
<span class={option.icon} aria-hidden="true" />
{/if}
<span style:text-transform="capitalize">{option.label}</span>
</button>
{/each}
{:else}
<div class="aw-select-group" use:melt={$groupEl(group.label)}>
<span class="aw-select-group-label" use:melt={$groupLabel(group.label)}>
{group.label}
</span>
{#each options as option}
<button class="aw-select-option" use:melt={$optionEl(option)}>
{#if option.icon}
@@ -99,6 +128,7 @@
</button>
{/each}
</div>
{/if}
{/each}
</div>
{/if}
@@ -110,19 +140,19 @@
{#if selectedOption?.icon}
<span class={selectedOption.icon} aria-hidden="true" />
{/if}
<select bind:value={selected}>
<select {id} bind:value>
{#each groups as group}
{@const isDefault = group.label === DEFAULT_GROUP}
{#if isDefault}
{#each options as option}
<option value={option.value} selected={option.value === selected}>
<option value={option.value} selected={option.value === value}>
{option.label}
</option>
{/each}
{:else}
<optgroup label={isDefault ? undefined : group.label}>
{#each options as option}
<option value={option.value} selected={option.value === selected}>
<option value={option.value} selected={option.value === value}>
{option.label}
</option>
{/each}
@@ -132,3 +162,9 @@
</select>
<span class="icon-cheveron-down" aria-hidden="true" />
</div>
<style lang="scss">
.aw-select {
min-width: var(--min-width, var(--p-select-min-width));
}
</style>

View File

@@ -22,4 +22,4 @@
];
</script>
<Select {options} bind:selected={$currentTheme} nativeMobile />
<Select {options} bind:value={$currentTheme} placement="top" />

1
src/lib/utils/anyify.ts Normal file
View File

@@ -0,0 +1 @@
export const anyify = (x: unknown) => x as any;

View File

@@ -2,15 +2,13 @@ import { writable } from 'svelte/store';
import type { Language } from './code';
import { browser } from '$app/environment';
export type Version = '1.4.x' | '1.3.x' | '1.2.x' | '1.1.x' | '1.0.x' | '0.15.x' | 'cloud';
export const versions: Readonly<Array<Omit<Version, 'cloud'>>> = [
'1.4.x',
'1.3.x',
'1.2.x',
'1.1.x',
'1.0.x',
'0.15.x'
] as const;
const allVersions = ['1.4.x', '1.3.x', '1.2.x', '1.1.x', '1.0.x', '0.15.x', 'cloud'] as const;
export type Version = (typeof allVersions)[number];
export const versions: Readonly<Array<Omit<Version, 'cloud'>>> = allVersions.filter(
(v) => v !== 'cloud'
);
export enum Service {
Account = 'account',

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { MainFooter } from '$lib/components';
import { MainFooter, Select } from '$lib/components';
import { DEFAULT_HOST } from '$lib/utils/metadata';
import { layoutState, toggleReferences } from '$lib/layouts/Docs.svelte';
import { parse } from '$lib/utils/markdown';
@@ -19,6 +19,7 @@
import { API_REFERENCE_TITLE_SUFFIX } from '$routes/titles.js';
import { getContext, onMount, setContext } from 'svelte';
import { writable } from 'svelte/store';
import { anyify } from '$lib/utils/anyify.js';
export let data;
@@ -40,18 +41,18 @@
}
});
function selectPlatform(event: Event & { currentTarget: EventTarget & HTMLSelectElement }) {
function selectPlatform(event: CustomEvent<unknown>) {
const { version, service } = $page.params;
const platform = event.currentTarget.value as Platform;
const platform = event.detail as Platform;
preferredPlatform.set(platform);
goto(`/docs/references/${version}/${event.currentTarget.value}/${service}`, {
goto(`/docs/references/${version}/${platform}/${service}`, {
noScroll: true
});
}
function selectVersion(event: Event & { currentTarget: EventTarget & HTMLSelectElement }) {
function selectVersion(event: CustomEvent<unknown>) {
const { platform, service } = $page.params;
const version = event.currentTarget.value as Version;
const version = event.detail as Version;
preferredVersion.set(version);
goto(`/docs/references/${version}/${platform}/${service}`, {
noScroll: true
@@ -99,37 +100,44 @@
<div class="u-flex u-gap-24 aw-u-color-text-primary">
<div class="u-flex u-cross-center u-gap-8">
<label class="u-small is-not-mobile" for="platform">Platform</label>
<div class="aw-select is-colored">
<select id="platform" on:change={selectPlatform} value={platform}>
<optgroup label="Client">
{#each Object.values(Platform).filter( (p) => p.startsWith('client-') ) as platform}
<option value={platform}>{platformMap[platform]}</option>
{/each}
</optgroup>
<optgroup label="Server">
{#each Object.values(Platform).filter( (p) => p.startsWith('server-') ) as platform}
<option value={platform}>{platformMap[platform]}</option>
{/each}
</optgroup>
</select>
<span class="icon-cheveron-down" aria-hidden="true" />
</div>
<Select
--min-width="10rem"
id="platform"
value={platform}
on:change={selectPlatform}
options={[
...Object.values(Platform)
.filter((p) => p.startsWith('client-'))
.map((p) => ({
value: p,
label: platformMap[p],
group: 'Client'
})),
...Object.values(Platform)
.filter((p) => p.startsWith('server-'))
.map((p) => ({
value: p,
label: platformMap[p],
group: 'Server'
}))
]}
nativeMobile
/>
</div>
<div class="u-flex u-cross-center u-gap-8">
<label class="u-small is-not-mobile" for="version">Version</label>
<div class="aw-select is-colored">
<select
id="version"
<Select
on:change={selectVersion}
value={$page.params.version}
>
<option value="cloud">Cloud</option>
{#each versions as version}
<option value={version}>{version}</option>
{/each}
</select>
<span class="icon-cheveron-down" aria-hidden="true" />
</div>
options={[
{ value: 'cloud', label: 'Cloud' },
...versions.map((version) => ({
value: version,
label: anyify(version)
}))
]}
/>
</div>
</div>
</div>

View File

@@ -1,8 +1,10 @@
@use '../abstract' as *;
.#{$p}-select {
--p-select-min-width: #{pxToRem(130)};
all:unset; display:flex; align-items:center; position:relative; line-height:1; user-select:none; cursor:pointer;
color:hsl(var(--aw-color-primary)); font-size:pxToRem(14); min-width: pxToRem(130); height: pxToRem(30);
color:hsl(var(--aw-color-primary)); font-size:pxToRem(14); min-width: var(--p-select-min-width); height: pxToRem(30);
select, .physical-select {
all:unset; display:flex; align-items:center;
@@ -129,6 +131,9 @@
padding-block: pxToRem(4);
box-sizing: border-box;
max-height: pxToRem(300) !important;
overflow-y: auto;
@mixin light-mode() {
--p-select-menu-bg-color: var(--aw-color-white);
--p-select-menu-border-color: var(--aw-color-black) / 0.06;
@@ -155,6 +160,12 @@
&-label {
margin-block-end: pxToRem(4);
padding-inline-start: pxToRem(4);
font-weight: 500;
}
.#{$p}-select-option {
padding-inline-start: pxToRem(16);
}
}
@@ -166,6 +177,7 @@
padding-block: pxToRem(4);
padding-inline: pxToRem(4);
border-radius: pxToRem(6);
scroll-margin-block: pxToRem(8);
cursor: pointer;