Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAM pages polishing #138

Merged
merged 19 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bun.lockb
Binary file not shown.
18 changes: 5 additions & 13 deletions src/lib/components/chart/rampricehistory.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,29 @@
import { Card, Stack } from '$lib/components/layout';
import { Asset } from '@wharfkit/antelope';
import Select from '../select/select.svelte';
import type { ExtendedSelectOption } from '../select/types';

interface Props {
data: { date: Date; value: Asset }[];
debug?: boolean;
}

let { data, debug = true }: Props = $props();
let { data }: Props = $props();

let ctx: HTMLCanvasElement;
let chart: Chart<'line'>;

const range = [
const range: ExtendedSelectOption[] = [
{ label: '1D', value: 1 },
{ label: '1W', value: 7 },
{ label: '1M', value: 30 },
{ label: '1Y', value: 365 }
];

let selectedRange = $state(range[1]);
let selectedRange: ExtendedSelectOption = $state(range[1]);

let dataRange = $derived.by(() => {
const rangeEndDate = dayjs(data[0].date);
const rangeStartDate = rangeEndDate.subtract(selectedRange.value, 'day');
debug && $inspect({ rangeStartDate, rangeEndDate });
const rangeStartDate = rangeEndDate.subtract(Number(selectedRange.value), 'day');
return data.filter(({ date }) => dayjs(date).isAfter(rangeStartDate));
});

Expand Down Expand Up @@ -96,10 +95,6 @@
legend: {
display: false
}
// decimation: {
// enabled: true,
// algorithm: 'lttb'
// }
}
}
});
Expand All @@ -110,9 +105,6 @@
chart.data.datasets[0].data = values;
chart.update();
});

debug &&
$inspect({ dataRangeLength: dataRange.length, currentDate: currentPoint.date, percentChange });
</script>

<Card>
Expand Down
3 changes: 3 additions & 0 deletions src/lib/components/select/elements/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as SelectMenu } from './menu.svelte';
export { default as SelectTrigger } from './trigger.svelte';
export { default as SelectItem } from './item.svelte';
53 changes: 53 additions & 0 deletions src/lib/components/select/elements/item.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script lang="ts">
import { melt } from '@melt-ui/svelte';
import { Check } from 'lucide-svelte';
import type { Readable } from 'svelte/store';
import type { ExtendedSelectOption } from '../types';

interface Props {
variant?: 'pill' | 'form';
id: string;
// Upstream bug: https://github.com/melt-ui/melt-ui/issues/974
// eslint-disable-next-line @typescript-eslint/no-explicit-any
option: any;
item: ExtendedSelectOption;
isSelected: Readable<(value: unknown) => boolean>;
}

const { option, isSelected, ...props }: Props = $props();
</script>

<div
class="
relative
grid
cursor-pointer
grid-cols-[16px_1fr]
items-center
gap-2
rounded-xl
px-2
py-1
font-medium
hover:bg-solar-500
hover:text-black/95
focus:z-10
focus:text-solar-950
data-[variant=form]:rounded-sm
data-[variant=pill]:rounded-xl
data-[highlighted=true]:bg-solar-500
data-[highlighted=true]:text-solar-950
data-[disabled]:opacity-50
"
data-variant={props.variant}
use:melt={$option(props.item)}
>
{#if props.item.image && typeof props.item.image === 'string'}
<img src={props.item.image} alt={props.item.label} class="mr-2 size-4 object-contain" />
{:else}
<div class="check">
<Check class="size-4 {$isSelected(props.item.value) ? 'block' : 'hidden'}" />
</div>
{/if}
{props.item.label}
</div>
45 changes: 45 additions & 0 deletions src/lib/components/select/elements/menu.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script lang="ts">
import { type AnyMeltElement, melt } from '@melt-ui/svelte';
import { type Snippet } from 'svelte';
import type { Readable } from 'svelte/store';
import { fade } from 'svelte/transition';
interface Props {
variant: 'pill' | 'form';
id: string;
children: Snippet;
open: Readable<boolean>;
menu: AnyMeltElement;
}

const { menu, ...props }: Props = $props();
</script>

{#if props.open}
<div
class="
z-10
flex
max-h-[300px]
flex-col
overflow-y-auto
border-2
border-mineShaft-600
bg-shark-950
py-1
shadow
focus:!ring-0
data-[variant=form]:rounded-lg
data-[variant=pill]:rounded-2xl
data-[variant=form]:px-2
data-[variant=form]:py-2
data-[variant=pill]:px-1
data-[variant=pill]:py-1
"
data-variant={props.variant}
use:melt={$menu}
transition:fade={{ duration: 100 }}
id={props.id + '-menu'}
>
{@render props.children()}
</div>
{/if}
53 changes: 53 additions & 0 deletions src/lib/components/select/elements/trigger.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script lang="ts">
import { type AnyMeltElement, melt } from '@melt-ui/svelte';
import { ChevronDown } from 'lucide-svelte';
import { type Snippet } from 'svelte';
import { type Readable } from 'svelte/store';

interface Props {
variant?: 'pill' | 'form';
id: string;
children: Snippet;
open: Readable<boolean>;
trigger: AnyMeltElement;
}

const { trigger, open, ...props }: Props = $props();
</script>

<button
class="
flex
items-center
justify-between
gap-2
border-2
border-mineShaft-600
bg-transparent
pl-4
pr-3
font-medium
transition-opacity
hover:opacity-90
focus:outline-2
focus:outline-solar-500
focus-visible:border-transparent
focus-visible:outline
data-[variant=pill]:h-10
data-[variant=form]:rounded-lg
data-[variant=pill]:rounded-full
data-[variant=form]:py-4
"
data-variant={props.variant}
use:melt={$trigger}
aria-label="{props.id}-label"
id={props.id}
>
<div class="flex items-center">
{@render props.children()}
</div>
<ChevronDown
data-open={$open}
class="size-5 transition-transform duration-100 data-[open=true]:rotate-180"
/>
</button>
141 changes: 21 additions & 120 deletions src/lib/components/select/select.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
<script lang="ts" context="module">
export interface OptionWithImage<T = unknown> extends SelectOption<T> {
image?: string;
}
</script>

<script lang="ts">
import { createSelect, createSync, melt, type SelectOption } from '@melt-ui/svelte';
import { fade } from 'svelte/transition';
import Check from 'lucide-svelte/icons/check';
import ChevronDown from 'lucide-svelte/icons/chevron-down';
import { createSelect, createSync } from '@melt-ui/svelte';
import type { ChangeFn } from '@melt-ui/svelte/internal/helpers';
import { SelectTrigger, SelectMenu, SelectItem } from './elements';
import type { ExtendedSelectOption, SelectOptionVariant } from './types';

interface Props {
options: OptionWithImage[];
selected: OptionWithImage;
variant?: 'pill' | 'form';
options: ExtendedSelectOption[];
selected: ExtendedSelectOption;
onSelectedChange?: ChangeFn<ExtendedSelectOption | undefined>;
variant?: SelectOptionVariant;
id: string;
onSelectedChange?: ChangeFn<SelectOption | undefined>;
required?: boolean;
disabled?: boolean;
multiple?: boolean;
Expand All @@ -40,7 +33,6 @@
states: { open, selected, selectedLabel },
helpers: { isSelected }
} = createSelect({
defaultSelected: _selected || options[0],
onSelectedChange,
required,
disabled,
Expand All @@ -53,120 +45,29 @@
}
});

// Sync the selected option with the passed in selected prop
const sync = createSync({ selected });
$effect(() => {
sync.selected(_selected, (v) => (_selected = v || options[0]));
});

/** Set the value from a parent */
export function set(option: OptionWithImage | null) {
if (!option) {
_selected = options[0];
} else {
_selected = option;
}
}

let selectedOption = $derived.by(() => options.find((o) => o.label === $selectedLabel));
// Get the whole option object
let selectedOption = $derived.by(
() => options.find((o) => o.label === $selectedLabel) || options[0]
);
</script>

<button
class="
flex
items-center
justify-between
gap-2
border-2
border-mineShaft-600
bg-transparent
pl-4
pr-3
font-medium
transition-opacity
hover:opacity-90
focus:outline-2
focus:outline-solar-500
focus-visible:border-transparent
focus-visible:outline
"
class:rounded-full={variant === 'pill'}
class:rounded-lg={variant === 'form'}
class:h-10={variant === 'pill'}
class:py-4={variant === 'form'}
use:melt={$trigger}
aria-label="{id}-label"
{id}
>
<div class="flex items-center">
{#if selectedOption?.image}
<img
src={selectedOption.image}
alt={selectedOption.label}
class="mr-2 size-5 object-contain"
/>
{/if}
{$selectedLabel || 'Select an option'}
</div>
<ChevronDown class="size-5 transition-transform duration-100 {$open ? 'rotate-180' : ''}" />
</button>
<SelectTrigger {variant} {id} {open} {trigger}>
{#if selectedOption.image && typeof selectedOption.image === 'string'}
<img src={selectedOption.image} alt={selectedOption.label} class="mr-2 size-5 object-contain" />
{/if}
{$selectedLabel || 'Select an option'}
</SelectTrigger>

{#if $open}
<div
class="
z-10
flex
max-h-[300px]
flex-col
overflow-y-auto
border-2
border-mineShaft-600
bg-shark-950
py-1
shadow
focus:!ring-0
"
class:px-1={variant === 'pill'}
class:px-2={variant === 'form'}
class:py-1={variant === 'pill'}
class:py-2={variant === 'form'}
class:rounded-2xl={variant === 'pill'}
class:rounded-lg={variant === 'form'}
use:melt={$menu}
transition:fade={{ duration: 100 }}
>
<SelectMenu {id} {variant} {menu} {open}>
{#each options as item}
<div
class="
relative
grid
cursor-pointer
grid-cols-[16px_1fr]
items-center
gap-2
rounded-xl
px-2
py-1
font-medium
hover:bg-solar-500
focus:z-10
focus:text-solar-950
data-[highlighted]:bg-solar-500
data-[highlighted]:text-solar-950
data-[disabled]:opacity-50
"
class:rounded-xl={variant === 'pill'}
class:rounded-sm={variant === 'form'}
use:melt={$option(item)}
>
{#if item.image}
<img src={item.image} alt={item.label} class="mr-2 size-4 object-contain" />
{:else}
<div class="check">
<Check class="size-4 {$isSelected(item.value) ? 'block' : 'hidden'}" />
</div>
{/if}
{item.label}
</div>
<SelectItem {id} {option} {variant} {item} {isSelected} />
{/each}
</div>
</SelectMenu>
{/if}
Loading
Loading