8.0k New

January 2024 - New Components

New Components in shadcn-svelte.

Epicenter

Local-first, open source apps

Special Sponsor

We've added four new components to the project, Carousel, Drawer, Sonner, & Pagination.

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-[12rem] sm: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 { toast } from "svelte-sonner";
	import * as Drawer from "$lib/components/ui/drawer/index.js";
	import * as Field from "$lib/components/ui/field/index.js";
	import * as RadioGroup from "$lib/components/ui/radio-group/index.js";
	import { IsMobile } from "$lib/components/hooks/is-mobile.svelte.js";
	import { Badge } from "$lib/components/ui/badge/index.js";
	import { Button, buttonVariants } from "$lib/components/ui/button/index.js";
 
	const deliveryTimes = [
		{
			value: "asap",
			id: "delivery-asap",
			label: "Standard delivery",
			description: "25–35 min · Driver assigned now",
			badge: "Fastest",
		},
		{
			value: "5-00",
			id: "delivery-5-00",
			label: "5:00 PM – 5:15 PM",
			description: "Prep starts at 4:45 PM",
		},
		{
			value: "5-30",
			id: "delivery-5-30",
			label: "5:30 PM – 5:45 PM",
			description: "Good if you're heading home",
		},
		{
			value: "6-00",
			id: "delivery-6-00",
			label: "6:00 PM – 6:15 PM",
			description: "Most popular · High demand",
		},
		{
			value: "6-30",
			id: "delivery-6-30",
			label: "6:30 PM – 6:45 PM",
			description: "Last slot before kitchen closes",
		},
	];
 
	let open = $state(false);
	let deliveryTime = $state("asap");
	const isMobile = new IsMobile();
 
	function handleConfirm() {
		const selected = deliveryTimes.find((time) => time.value === deliveryTime);
 
		if (!selected) {
			return;
		}
 
		open = false;
		toast("Delivery time confirmed", {
			description: selected.label,
		});
	}
</script>
 
<Drawer.Root bind:open direction={isMobile.current ? "bottom" : "right"}>
	<Drawer.Trigger class={buttonVariants({ variant: "secondary" })}>Open Drawer</Drawer.Trigger>
	<Drawer.Content>
		<Drawer.Header>
			<Drawer.Title>Pick a delivery time</Drawer.Title>
			<Drawer.Description>We&apos;ll prepare your order as soon as possible.</Drawer.Description>
		</Drawer.Header>
		<div class="flex-1 overflow-y-auto p-4">
			<RadioGroup.Root bind:value={deliveryTime} class="gap-2">
				{#each deliveryTimes as time (time.value)}
					<Field.Label for={time.id}>
						<Field.Field orientation="horizontal">
							<Field.Content>
								<Field.Title class="flex items-center gap-2">
									{time.label}
									{#if time.badge}
										<Badge variant="secondary">{time.badge}</Badge>
									{/if}
								</Field.Title>
								<Field.Description>{time.description}</Field.Description>
							</Field.Content>
							<RadioGroup.Item value={time.value} id={time.id} />
						</Field.Field>
					</Field.Label>
				{/each}
			</RadioGroup.Root>
		</div>
		<Drawer.Footer>
			<Button onclick={handleConfirm} class="h-[34px]">Confirm Delivery Time</Button>
			<Drawer.Close class={buttonVariants({ variant: "outline" })}>Cancel</Drawer.Close>
		</Drawer.Footer>
	</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} perPage={10} 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.