January 2024 - New Components
New Components in shadcn-svelte.
Epicenter
Local-first, open source apps
We've added four new components to the project, Carousel, Drawer, Sonner, & Pagination.
New Component: Carousel
1
2
3
4
5
<script lang="ts">
import * as Card from "$lib/components/ui/card/index.js";
import * as Carousel from "$lib/components/ui/carousel/index.js";
</script>
<Carousel.Root class="w-full max-w-xs">
<Carousel.Content>
{#each Array(5), i}
<Carousel.Item>
<div class="p-1">
<Card.Root>
<Card.Content
class="flex aspect-square items-center justify-center p-6"
>
<span class="text-4xl font-semibold">{i + 1}</span>
</Card.Content>
</Card.Root>
</div>
</Carousel.Item>
{/each}
</Carousel.Content>
<Carousel.Previous />
<Carousel.Next />
</Carousel.Root> New Component: Drawer
<script lang="ts">
import MinusIcon from "@lucide/svelte/icons/minus";
import PlusIcon from "@lucide/svelte/icons/plus";
import * as Drawer from "$lib/components/ui/drawer/index.js";
import { Button, buttonVariants } from "$lib/components/ui/button/index.js";
import { BarChart } from "layerchart";
import { scaleBand } from "d3-scale";
import { cubicInOut } from "svelte/easing";
const data = [
{
goal: 400
},
{
goal: 300
},
{
goal: 200
},
{
goal: 300
},
{
goal: 200
},
{
goal: 278
},
{
goal: 189
},
{
goal: 239
},
{
goal: 300
},
{
goal: 200
},
{
goal: 278
},
{
goal: 189
},
{
goal: 349
}
];
let goal = $state(350);
function handleClick(adjustment: number) {
goal = Math.max(200, Math.min(400, goal + adjustment));
}
</script>
<Drawer.Root>
<Drawer.Trigger class={buttonVariants({ variant: "outline" })}
>Open Drawer</Drawer.Trigger
>
<Drawer.Content>
<div class="mx-auto w-full max-w-sm">
<Drawer.Header>
<Drawer.Title>Move Goal</Drawer.Title>
<Drawer.Description>Set your daily activity goal.</Drawer.Description>
</Drawer.Header>
<div class="p-4 pb-0">
<div class="flex items-center justify-center space-x-2">
<Button
variant="outline"
size="icon"
class="size-8 shrink-0 rounded-full"
onclick={() => handleClick(-10)}
disabled={goal <= 200}
>
<MinusIcon />
<span class="sr-only">Decrease</span>
</Button>
<div class="flex-1 text-center">
<div class="text-7xl font-bold tracking-tighter">
{goal}
</div>
<div class="text-muted-foreground text-[0.70rem] uppercase">
Calories/day
</div>
</div>
<Button
variant="outline"
size="icon"
class="size-8 shrink-0 rounded-full"
onclick={() => handleClick(10)}
disabled={goal >= 400}
>
<PlusIcon />
<span class="sr-only">Increase</span>
</Button>
</div>
<div class="mt-3 h-[120px]">
<div class="h-full w-full">
<BarChart
data={data.map((d, i) => ({ goal: d.goal, index: i }))}
y="goal"
x="index"
xScale={scaleBand().padding(0.25)}
axis={false}
tooltipContext={false}
props={{
bars: {
stroke: "none",
rounded: "all",
radius: 4,
motion: { type: "tween", duration: 500, easing: cubicInOut },
fill: "var(--color-foreground)",
fillOpacity: 0.9
},
highlight: { area: { fill: "none" } }
}}
/>
</div>
</div>
</div>
<Drawer.Footer>
<Button>Submit</Button>
<Drawer.Close class={buttonVariants({ variant: "outline" })}
>Cancel</Drawer.Close
>
</Drawer.Footer>
</div>
</Drawer.Content>
</Drawer.Root> The Drawer is built on top of vaul-svelte and is a port of vaul, originally created by Emil Kowalski for React.
New Component: Sonner
<script lang="ts">
import { toast } from "svelte-sonner";
import { Button } from "$lib/components/ui/button/index.js";
</script>
<Button
variant="outline"
onclick={() =>
toast("Event has been created", {
description: "Sunday, December 03, 2023 at 9:00 AM",
action: {
label: "Undo",
onClick: () => console.info("Undo")
}
})}
>
Show Toast
</Button> The Sonner component is provided by svelte-sonner, which is a Svelte port of Sonner, originally created by Emil Kowalski for React.
New Component: Pagination
<script lang="ts">
import * as Pagination from "$lib/components/ui/pagination/index.js";
</script>
<Pagination.Root count={30} page={2}>
{#snippet children({ pages, currentPage })}
<Pagination.Content>
<Pagination.Item>
<Pagination.Previous />
</Pagination.Item>
{#each pages as page (page.key)}
{#if page.type === "ellipsis"}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
{:else}
<Pagination.Item>
<Pagination.Link {page} isActive={currentPage === page.value}>
{page.value}
</Pagination.Link>
</Pagination.Item>
{/if}
{/each}
<Pagination.Item>
<Pagination.Ellipsis />
</Pagination.Item>
<Pagination.Item>
<Pagination.Next />
</Pagination.Item>
</Pagination.Content>
{/snippet}
</Pagination.Root> Pagination leverages the Pagination component from Bits UI.