Adding skeleton component from carbon, adding wishlist button to game page, fixing mapping, and css styles.

This commit is contained in:
Bradley Shellnut 2022-11-03 00:16:52 -05:00
parent 06a7d342a3
commit ea32e9e6ff
10 changed files with 215 additions and 43 deletions

View file

@ -0,0 +1,115 @@
<!-- Taken from carbon design system svelte -->
<!-- https://github.com/carbon-design-system/carbon-components-svelte/blob/master/src/SkeletonPlaceholder/SkeletonPlaceholder.svelte -->
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<div
class:bx--skeleton__placeholder={true}
{...$$restProps}
on:click
on:mouseover
on:mouseenter
on:mouseleave
/>
<style lang="scss">
@keyframes skeleton {
0% {
opacity: 0.3;
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: left;
transform-origin: left;
}
20% {
opacity: 1;
-webkit-transform: scaleX(1);
transform: scaleX(1);
-webkit-transform-origin: left;
transform-origin: left;
}
28% {
-webkit-transform: scaleX(1);
transform: scaleX(1);
-webkit-transform-origin: right;
transform-origin: right;
}
51% {
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: right;
transform-origin: right;
}
58% {
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: right;
transform-origin: right;
}
82% {
-webkit-transform: scaleX(1);
transform: scaleX(1);
-webkit-transform-origin: right;
transform-origin: right;
}
83% {
-webkit-transform: scaleX(1);
transform: scaleX(1);
-webkit-transform-origin: left;
transform-origin: left;
}
96% {
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: left;
transform-origin: left;
}
100% {
opacity: 0.3;
-webkit-transform: scaleX(0);
transform: scaleX(0);
-webkit-transform-origin: left;
transform-origin: left;
}
}
.bx--skeleton__placeholder {
--cds-skeleton-01: #474747;
position: relative;
padding: 0;
border: none;
background: var(--cds-skeleton-01, #e5e5e5);
box-shadow: none;
pointer-events: none;
width: 6.25rem;
height: 6.25rem;
}
.bx--skeleton__placeholder:hover,
.bx--skeleton__placeholder:focus,
.bx--skeleton__placeholder:active {
border: none;
cursor: default;
outline: none;
}
.bx--skeleton__placeholder::before {
--cds-skeleton-02: #525252;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-animation: 3000ms ease-in-out skeleton infinite;
animation: 3000ms ease-in-out skeleton infinite;
background: var(--cds-skeleton-02, #c6c6c6);
content: '';
will-change: transform-origin, transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
.bx--skeleton__placeholder::before {
-webkit-animation: none;
animation: none;
}
}
</style>

View file

@ -10,7 +10,6 @@
import { browser } from '$app/environment';
export let game: GameType | SavedGameType;
export let minimal: boolean = false;
export let detailed: boolean = false;
const dispatch = createEventDispatcher();
@ -23,6 +22,24 @@
dispatch('handleRemoveCollection', game);
}
// Naive and assumes description is only on our GameType at the moment
function isGameType(game: GameType | SavedGameType): game is GameType {
return (game as GameType).description !== undefined;
}
// function lazy(img: HTMLImageElement) {
// function loaded() {
// img.classList.add('loaded');
// img.classList.remove('loading');
// }
// if (img.complete) {
// loaded();
// } else {
// img.classList.add('loading');
// img.onload = () => loaded();
// }
// }
$: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id);
$: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id);
</script>
@ -31,15 +48,16 @@
<h2>{game.name}</h2>
<a class="thumbnail" href={`/game/${game.id}`}>
<img src={game.thumb_url} alt={`Image of ${game.name}`} />
<!-- loading="lazy" decoding="async" -->
</a>
<div class="game-details">
<p>Players: {game.min_players} - {game.max_players}</p>
<p>Players: {game.players}</p>
<p>Time: {game.playtime} minutes</p>
{#if game?.min_age}
{#if isGameType(game) && game?.min_age}
<p>Min Age: {game.min_age}</p>
{/if}
{#if detailed}
{#if detailed && isGameType(game) && game?.description}
<div class="description">{@html game.description}</div>
{/if}
</div>

View file

@ -20,7 +20,7 @@
// const responseData = await response.json();
// console.log('responseData', responseData);
// gameStore.add(responseData?.game);
const game = gameStore.add(mapSavedGameToGame(randomGame));
gameStore.add(mapSavedGameToGame(randomGame));
boredState.update((n) => ({ ...n, loading: false }));
} else {
toast.send('Error!', { duration: 3000, type: ToastType.ERROR, dismissible: true });

View file

@ -5,11 +5,11 @@
export let form: ActionData;
console.log('advanced search form', form);
let submitting = $boredState?.loading;
let minAge = 1;
let minPlayers = 1;
let maxPlayers = 1;
let exactMinPlayers = false;
let exactMaxPlayers = false;
let minAge = form?.minAge || 1;
let minPlayers = form?.minPlayers || 1;
let maxPlayers = form?.maxPlayers || 1;
let exactMinPlayers = form?.exactMinPlayers || false;
let exactMaxPlayers = form?.exactMaxPlayers || false;
</script>
<fieldset class="advanced-search" aria-busy={submitting} disabled={submitting}>

View file

@ -39,9 +39,8 @@ export type SavedGameType = {
id: string;
name: string;
thumb_url: string;
min_players: number;
max_players: number;
playtime: string
players: string;
playtime: string;
}
export type GameType = {

View file

@ -1,19 +1,46 @@
import type { GameType, SavedGameType } from '$lib/types';
export function convertToSavedGame(game: GameType | SavedGameType): SavedGameType {
return {
id: game.id,
name: game.name,
thumb_url: game.thumb_url,
players: game.players,
playtime: game.playtime
};
}
export function mapSavedGameToGame(game: SavedGameType): GameType {
const { id, name, thumb_url, min_players, max_players, playtime } = game;
const { id, name, thumb_url, players, playtime } = game;
return {
id,
handle: '',
name,
url: '',
edit_url: '',
thumb_url,
min_players,
max_players,
image_url: '',
price: 0,
price_ca: 0,
price_uk: 0,
price_au: 0,
msrp: 0,
year_published: new Date().getFullYear(),
min_players: 0,
max_players: 0,
min_playtime: 0,
max_playtime: 0,
min_age: 0,
description: '',
description_preview: '',
players,
playtime
};
}
export function mapAPIGameToBoredGame(game): GameType {
// TODO: TYpe API response
export function mapAPIGameToBoredGame(game: any): GameType {
const {
id,
handle,

View file

@ -1,14 +1,7 @@
import { collectionStore } from '$lib/stores/collectionStore';
import { toast } from '$lib/components/toast/toast';
import { ToastType, type GameType, type SavedGameType } from '$lib/types';
function convertToSavedGame(game: GameType | SavedGameType): SavedGameType {
return {
id: game.id,
name: game.name,
thumb_url: game.thumb_url,
};
}
import { convertToSavedGame } from './gameMapper';
export function addToCollection(game: GameType | SavedGameType) {
collectionStore.add(convertToSavedGame(game));

View file

@ -1,14 +1,7 @@
import { wishlistStore } from '$lib/stores/wishlistStore';
import { toast } from '$lib/components/toast/toast';
import { ToastType, type GameType, type SavedGameType } from '$lib/types';
function convertToSavedGame(game: GameType | SavedGameType): SavedGameType {
return {
id: game.id,
name: game.name,
thumb_url: game.thumb_url,
};
}
import { convertToSavedGame } from './gameMapper';
export function addToWishlist(game: GameType | SavedGameType) {
wishlistStore.add(convertToSavedGame(game));

View file

@ -12,6 +12,7 @@
import Random from '$lib/components/random/index.svelte';
import Pagination from '$lib/components/pagination/index.svelte';
import RemoveWishlistDialog from '$root/lib/components/dialog/RemoveWishlistDialog.svelte';
import SkeletonPlaceholder from '$root/lib/components/SkeletonPlaceholder.svelte';
export let data: PageData;
export let form: ActionData;
@ -130,6 +131,7 @@
</section>
</div>
<SkeletonPlaceholder style="height: 12rem; width: 12rem; border-radius: 10px;" />
{#if $gameStore?.length > 0}
<div class="games">
<h1>Games Found:</h1>
@ -138,7 +140,6 @@
<Game
on:handleRemoveWishlist={handleRemoveWishlist}
on:handleRemoveCollection={handleRemoveCollection}
minimal
{game}
/>
{/each}

View file

@ -9,6 +9,7 @@
} from '@rgossiaux/svelte-heroicons/outline';
import type { GameType, SavedGameType } from '$lib/types';
import { collectionStore } from '$lib/stores/collectionStore';
import { wishlistStore } from '$lib/stores/wishlistStore';
import Button from '$lib/components/button/index.svelte';
import RemoveCollectionDialog from '$root/lib/components/dialog/RemoveCollectionDialog.svelte';
import { addToCollection } from '$lib/util/manipulateCollection';
@ -16,30 +17,34 @@
import { boredState } from '$root/lib/stores/boredState';
import { browser } from '$app/environment';
import LinkWithIcon from '$root/lib/components/LinkWithIcon.svelte';
import { addToWishlist } from '$root/lib/util/manipulateWishlist';
import RemoveWishlistDialog from '$root/lib/components/dialog/RemoveWishlistDialog.svelte';
$: existsInCollection = $collectionStore.find((item: SavedGameType) => item.id === game.id);
$: existsInWishlist = $wishlistStore.find((item: SavedGameType) => item.id === game.id);
export let data: PageData;
export let game: GameType = data?.game;
console.log('page game', game);
let seeMore: boolean = false;
console.log(game?.description?.indexOf('</p>'));
let firstParagraphEnd = 0;
if (game?.description?.indexOf('</p>') > 0) {
firstParagraphEnd = game?.description?.indexOf('</p>') + 4;
} else if (game?.description?.indexOf('</ p>') > 0) {
firstParagraphEnd = game?.description?.indexOf('</ p>') + 5;
}
console.log('firstParagraphEnd', firstParagraphEnd);
function removeGame() {
function remoceFromCollection() {
boredState.update((n) => ({
...n,
dialog: { isOpen: true, content: RemoveCollectionDialog, additionalData: game }
}));
if (browser) {
localStorage.collection = JSON.stringify($collectionStore);
}
}
function removeFromWishList() {
boredState.update((n) => ({
...n,
dialog: { isOpen: true, content: RemoveWishlistDialog, additionalData: game }
}));
}
</script>
@ -55,7 +60,7 @@
<img src={game.image_url} alt={`Image of ${game.name}`} />
</a>
</div>
<div style="display: grid; place-items: center;">
<div style="display: grid; place-items: center; gap: 2rem;">
<div class="details">
<p>Year: {game?.year_published}</p>
<p>Players: {game.players}</p>
@ -70,7 +75,7 @@
</div>
<div>
{#if existsInCollection}
<Button size="md" kind="danger" icon on:click={() => removeGame()}>
<Button size="md" kind="danger" icon on:click={() => remoceFromCollection()}>
Remove from collection <MinusCircleIcon width="24" height="24" />
</Button>
{:else}
@ -89,6 +94,27 @@
</Button>
{/if}
</div>
<div>
{#if existsInWishlist}
<Button size="md" kind="danger" icon on:click={() => removeFromWishList()}>
Remove from wishlist <MinusCircleIcon width="24" height="24" />
</Button>
{:else}
<Button
size="md"
kind="primary"
icon
on:click={() => {
addToWishlist(game);
if (browser) {
localStorage.wishlist = JSON.stringify($wishlistStore);
}
}}
>
Add to wishlist <PlusCircleIcon width="24" height="24" />
</Button>
{/if}
</div>
</div>
</section>
{#if firstParagraphEnd > 0}