Skip to content

Commit

Permalink
[ScrollingValue] Add axis prop to control direction, and support <s…
Browse files Browse the repository at this point in the history
…lot> rendering of value for custom component display
  • Loading branch information
techniq committed Nov 22, 2023
1 parent 9021816 commit 81b1f33
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/nine-rings-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-ux': patch
---

[ScrollingValue] Add `axis` prop to control direction, and support <slot> rendering of value for custom component display
20 changes: 16 additions & 4 deletions packages/svelte-ux/src/lib/components/ScrollingValue.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
export let value = 0;
export let single = false;
export let format: (value: number) => string | number = (value) => value;
export let axis: 'x' | 'y' = 'y';
export let classes: {
root?: string;
Expand All @@ -19,6 +20,9 @@
// const displayValue = tweened(value, { duration: 1000, easing: bounceOut });
$: $displayValue = value;
$: offset = modulo($displayValue, 1);
$: nextDisplayValue = Math.floor(single && $displayValue >= 9 ? 0 : $displayValue + 1);
$: currentDisplayValue = Math.floor($displayValue);
</script>

<div
Expand All @@ -32,14 +36,22 @@
>
<div
class={cls('col-span-full row-span-full', theme.value, classes.value)}
style:transform="translateY({-100 + 100 * offset}%)"
style:transform={axis === 'x'
? `translateX(${100 + 100 * -offset}%)`
: `translateY(${-100 + 100 * offset}%)`}
>
{format(Math.floor(single && $displayValue >= 9 ? 0 : $displayValue + 1))}
<slot value={nextDisplayValue}>
{format(nextDisplayValue)}
</slot>
</div>
<div
class={cls('col-span-full row-span-full', theme.value, classes.value)}
style:transform="translateY({100 * offset}%)"
style:transform={axis === 'x'
? `translateX(${100 * -offset}%)`
: `translateY(${100 * offset}%)`}
>
{format(Math.floor($displayValue))}
<slot value={currentDisplayValue}>
{format(currentDisplayValue)}
</slot>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
<script lang="ts">
import Preview from '$lib/components/Preview.svelte';
import ScrollingValue from '$lib/components/ScrollingValue.svelte';
import { mdiChevronLeft, mdiChevronRight, mdiMinus, mdiPlus } from '@mdi/js';
import { addMonths, startOfMonth } from 'date-fns';
import Button from '$lib/components/Button.svelte';
import Field from '$lib/components/Field.svelte';
import { mdiMinus, mdiPlus } from '@mdi/js';
import ButtonGroup from '$lib/components/ButtonGroup.svelte';
import { romanize, timerStore } from '$lib';
import Field from '$lib/components/Field.svelte';
import Month from '$lib/components/Month.svelte';
import Preview from '$lib/components/Preview.svelte';
import ScrollingValue from '$lib/components/ScrollingValue.svelte';
import ToggleGroup from '$lib/components/ToggleGroup.svelte';
import ToggleOption from '$lib/components/ToggleOption.svelte';
import { PeriodType, format, romanize, timerStore } from '$lib';
let value = 0;
let axis: 'x' | 'y' = 'x';
const timer = timerStore({
initial: 60,
Expand All @@ -27,10 +34,12 @@
const step = e.shiftKey ? 10 : e.altKey ? 100 : 1;
switch (e.code) {
case 'ArrowUp':
case 'ArrowRight':
value += step;
e.preventDefault();
break;
case 'ArrowDown':
case 'ArrowLeft':
value -= step;
e.preventDefault();
break;
Expand All @@ -42,36 +51,47 @@
delay: 2000,
onTick: (value) => (value ?? 0) + 1,
});
const firstOfMonth = startOfMonth(new Date());
</script>

<svelte:window on:keydown={onKeyDown} />

<h1>Examples</h1>

<ButtonGroup variant="fill" class="grid grid-flow-col ml-2">
<Button on:click={() => (value -= 100)}>-100</Button>
<Button on:click={() => (value -= 10)}>-10</Button>
<Button on:click={() => (value -= 1)}>-1</Button>
<Button on:click={() => (value = 0)}>0</Button>
<Button on:click={() => (value += 1)}>+1</Button>
<Button on:click={() => (value += 10)}>+10</Button>
<Button on:click={() => (value += 100)}>+100</Button>
</ButtonGroup>
<span class="text-xs ml-2 text-black/50"
>also keyboard up/down with shift: +/- 10 option: +/- 100
</span>
<div class="grid grid-cols-[1fr,140px] items-center gap-6">
<ButtonGroup variant="fill" class="grid grid-flow-col ml-2">
<Button on:click={() => (value -= 100)}>-100</Button>
<Button on:click={() => (value -= 10)}>-10</Button>
<Button on:click={() => (value -= 1)}>-1</Button>
<Button on:click={() => (value = 0)}>0</Button>
<Button on:click={() => (value += 1)}>+1</Button>
<Button on:click={() => (value += 10)}>+10</Button>
<Button on:click={() => (value += 100)}>+100</Button>
</ButtonGroup>

<Field label="axis" labelPlacement="left">
<ToggleGroup bind:value={axis} variant="fill-white" inset>
<ToggleOption value="x">x</ToggleOption>
<ToggleOption value="y">y</ToggleOption>
</ToggleGroup>
</Field>
</div>
<div class="text-xs ml-2 text-black/50">
also keyboard up/down with shift: +/- 10 option: +/- 100
</div>

<h2>Basic</h2>

<Preview>
<ScrollingValue bind:value />
<ScrollingValue bind:value {axis} />
</Preview>

<h2>Individual numbers</h2>

<Preview>
{#each Math.abs(value).toString().split('') as num}
<ScrollingValue value={Number(num)} single class="text-3xl tabular-nums" />
<ScrollingValue value={Number(num)} {axis} single class="text-3xl tabular-nums" />
{/each}
</Preview>

Expand All @@ -80,6 +100,7 @@
<Preview>
<ScrollingValue
bind:value
{axis}
format={(value) => romanize(Math.abs(value)) || 0}
class="text-3xl tabular-nums"
/>
Expand All @@ -91,6 +112,7 @@
<span class="text-3xl">
<span class="font-semibold">Svelte</span> is
<ScrollingValue
{axis}
value={$indexTimer}
format={(value) => {
const options = ['compiled', 'compact', 'complete'];
Expand All @@ -109,7 +131,7 @@

<Preview>
<Field label="Value">
<ScrollingValue bind:value class="w-full" />
<ScrollingValue bind:value {axis} class="w-full" />
<div slot="append" class="flex">
<Button icon={mdiMinus} on:click={() => (value -= 1)} size="sm" />
<Button icon={mdiPlus} on:click={() => (value += 1)} size="sm" />
Expand All @@ -125,7 +147,7 @@
<div slot="prepend" class="flex">
<Button icon={mdiMinus} on:click={() => (value -= 1)} size="sm" />
</div>
<ScrollingValue bind:value classes={{ root: 'w-full', value: 'w-full text-center' }} />
<ScrollingValue bind:value {axis} classes={{ root: 'w-full', value: 'w-full text-center' }} />
<div slot="append" class="flex">
<Button icon={mdiPlus} on:click={() => (value += 1)} size="sm" />
</div>
Expand All @@ -139,41 +161,74 @@
<ButtonGroup variant="outline">
<Button icon={mdiMinus} on:click={() => (value -= 1)} size="sm" iconOnly={false} />
<Button class="w-20 pointer-events-none">
<ScrollingValue bind:value classes={{ root: 'w-full', value: 'w-full text-center' }} />
<ScrollingValue
bind:value
{axis}
classes={{ root: 'w-full', value: 'w-full text-center' }}
/>
</Button>
<Button icon={mdiPlus} on:click={() => (value += 1)} size="sm" iconOnly={false} />
</ButtonGroup>

<ButtonGroup variant="fill">
<Button icon={mdiMinus} on:click={() => (value -= 1)} size="sm" iconOnly={false} />
<Button class="w-20 pointer-events-none">
<ScrollingValue bind:value classes={{ root: 'w-full', value: 'w-full text-center' }} />
<ScrollingValue
bind:value
{axis}
classes={{ root: 'w-full', value: 'w-full text-center' }}
/>
</Button>
<Button icon={mdiPlus} on:click={() => (value += 1)} size="sm" iconOnly={false} />
</ButtonGroup>

<ButtonGroup color="accent" variant="fill-light">
<Button icon={mdiMinus} on:click={() => (value -= 1)} size="sm" iconOnly={false} />
<Button class="w-20 pointer-events-none">
<ScrollingValue bind:value classes={{ root: 'w-full', value: 'w-full text-center' }} />
<ScrollingValue
bind:value
{axis}
classes={{ root: 'w-full', value: 'w-full text-center' }}
/>
</Button>
<Button icon={mdiPlus} on:click={() => (value += 1)} size="sm" iconOnly={false} />
</ButtonGroup>

<ButtonGroup color="accent" variant="fill-outline">
<Button icon={mdiMinus} on:click={() => (value -= 1)} size="sm" iconOnly={false} />
<Button class="w-20 pointer-events-none">
<ScrollingValue bind:value classes={{ root: 'w-full', value: 'w-full text-center' }} />
<ScrollingValue
bind:value
{axis}
classes={{ root: 'w-full', value: 'w-full text-center' }}
/>
</Button>
<Button icon={mdiPlus} on:click={() => (value += 1)} size="sm" iconOnly={false} />
</ButtonGroup>
</div>
</Preview>

<h2>Slot</h2>

<Preview>
{@const startOfMonth = addMonths(firstOfMonth, value)}
<div class="grid w-96">
<div class="grid grid-cols-[auto,1fr,auto] items-center justify-items-center">
<Button icon={mdiChevronLeft} class="p-2" on:click={() => (value -= 1)} />
<div>{format(startOfMonth, PeriodType.Month)}</div>
<Button icon={mdiChevronRight} class="p-2" on:click={() => (value += 1)} />
</div>
<ScrollingValue {value} {axis} let:value>
{@const startOfMonth = addMonths(firstOfMonth, value)}
<Month {startOfMonth} hideControls />
</ScrollingValue>
</div>
</Preview>

<h2>Countdown</h2>

<Preview>
<ScrollingValue value={$timer ?? 0} class="text-6xl tabular-nums" />
<ScrollingValue value={$timer ?? 0} {axis} class="text-6xl tabular-nums" />
<ButtonGroup variant="fill" class="ml-3">
<Button on:click={timer.start} disabled={$isRunning}>Start</Button>
<Button on:click={timer.stop} disabled={!$isRunning}>Stop</Button>
Expand All @@ -186,6 +241,7 @@
<Preview>
<ScrollingValue
bind:value
{axis}
classes={{ value: 'text-6xl first:bg-red-500/50 last:bg-green-500/50' }}
/>
<div class="grid grid-flow-col">
Expand Down

1 comment on commit 81b1f33

@vercel
Copy link

@vercel vercel bot commented on 81b1f33 Nov 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.