Advent of SvelteKit 2022

Drag the presents under the tree!

Challenge

Source code

+page.svelte

<script lang="ts">
	import ChristmasTree from './ChristmasTree.svelte';
	import ChristmasPresent from './ChristmasPresent.svelte';
	import autoAnimate from '@formkit/auto-animate';

	let presents = ['small-red-gift', 'blue-gift', 'tall-red-gift'];
	let underTheTree: string[] = [];

	function startDrag(event: DragEvent, index: number) {
		console.log('start', index);
		const dt = event.dataTransfer!;
		dt.dropEffect = 'move';
		dt.effectAllowed = 'move';
		dt.setData('index', index);
	}
	function onDrop(event: DragEvent) {
		const index = event.dataTransfer!.getData('index');
		const [removed] = presents.splice(index, 1);
		underTheTree.push(removed);
		presents = presents;
		underTheTree = underTheTree;
	}
</script>

<h1>Drag the presents under the tree!</h1>
<!-- Component event forwarding! -->
<ChristmasTree presents={underTheTree} on:drop={onDrop} />
<div class="wrapper">
	<div class="presents" use:autoAnimate>
		{#each presents as p, idx (p)}
			<ChristmasPresent name={p} on:dragstart={(e) => startDrag(e, idx)} />
		{/each}
	</div>
</div>

<style>
	.wrapper {
		padding-top: 2rem;
		margin-top: 4rem;
		background: var(--gray-1);
		width: 100%;
	}
	.presents {
		display: flex;
		align-items: flex-end;
		justify-content: center;
	}
</style>

+page.ts

export const prerender = true;

ChristmasPresent.svelte

<script lang="ts">
	import tallRedGift from './tall-red-gift.svg?raw';
	import smallRedGift from './small-red-gift.svg?raw';
	import blueGift from './blue-gift.svg?raw';

	export let name: string;

	const map: Record<string, string> = {
		'tall-red-gift': tallRedGift,
		'small-red-gift': smallRedGift,
		'blue-gift': blueGift
	};
</script>

<!-- TIL: draggable is enumerated, so "true" and "false" -->
<div draggable="true" on:dragstart>
	{@html map[name]}
</div>

<style>
	div :global(svg) {
		width: 8rem;
	}
</style>

ChristmasTree.svelte

<script lang="ts">
	import tree from './tree.svg?raw';
	import autoAnimate from '@formkit/auto-animate';
	import ChristmasPresent from './ChristmasPresent.svelte';
	export let presents: string[];
</script>

<div class="container" on:drop on:dragover|preventDefault on:dragenter|preventDefault>
	{@html tree}
	<div class="presents" use:autoAnimate>
		{#each presents as p (p)}
			<ChristmasPresent name={p} />
		{/each}
	</div>
</div>

<style>
	.container {
		position: relative;
		display: flex;
		justify-content: center;
	}

	.container > :global(svg) {
		width: 16rem;
	}

	.presents {
		display: flex;
		align-items: flex-end;
		position: absolute;
		bottom: -2rem;
		gap: -2rem;
	}

	/* Styles the middle present slightly in front of the other two */
	.presents > :global(div):nth-of-type(2) {
		margin-left: -2rem;
		margin-right: -2rem;
		z-index: var(--layer-2);
		margin-bottom: -1rem;
	}
</style>