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

File diff suppressed because it is too large Load Diff

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,11 @@
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}
{#if isDefault}
{#each options as option}
<button class="aw-select-option" use:melt={$optionEl(option)}>
{#if option.icon}
@@ -98,7 +113,22 @@
<span style:text-transform="capitalize">{option.label}</span>
</button>
{/each}
</div>
{: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}
<span class={option.icon} aria-hidden="true" />
{/if}
<span style:text-transform="capitalize">{option.label}</span>
</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"
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>
<Select
on:change={selectVersion}
value={$page.params.version}
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;