Adding components.

This commit is contained in:
Bradley Shellnut 2022-07-04 22:38:49 -07:00
parent 9c1e2e9be5
commit 85459d531e
6 changed files with 584 additions and 0 deletions

View file

@ -0,0 +1,80 @@
<script lang="ts">
export let labelText: string;
export let showLabel: boolean;
</script>
<label htmlFor={`${guest.id}`} className="checkbox">
<span class="checkbox__input">
<input
id={`${guest.id}`}
name={`${guest.id}`}
checked={inputs[`${guest.id}`].plusOne}
onChange={onChangePlusOne}
type="checkbox"
/>
<span class="checkbox__control">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
aria-hidden="true"
focusable="false"
>
<path
fill="none"
stroke="currentColor"
strokeWidth="3"
d="M1.73 12.91l6.37 6.37L22.79 4.59"
/>
</svg>
</span>
</span>
<span class="checkbox__label">Plus one? </span>
</label>
<style lang="scss">
.checkbox {
display: grid;
grid-template-columns: min-content auto;
align-items: center;
align-content: center;
gap: 0.5rem;
font-size: 2rem;
.checkbox__input {
display: grid;
grid-template-areas: 'checkbox';
> * {
grid-area: checkbox;
}
}
.checkbox__input input:checked + .checkbox__control svg {
transform: scale(1);
}
.checkbox__input input:focus + .checkbox__control {
box-shadow: var(--level-2-primary);
}
.checkbox__control {
display: inline-grid;
width: 1em;
height: 1em;
border-radius: 0.1em;
border: 0.1em solid var(--lightViolet);
svg {
transition: transform 0.1s ease-in 25ms;
transform: scale(0);
transform-origin: bottom left;
}
}
input[type='checkbox'] {
opacity: 0;
width: 1em;
height: 1em;
}
}
</style>

View file

@ -0,0 +1,112 @@
<script>
/**
* @event {boolean} check
*/
/**
* Specify the value of the checkbox
* @type {any}
*/
export let value = '';
/** Specify whether the checkbox is checked */
export let checked = false;
/**
* Specify the bound group
* @type {any[]}
*/
export let group = undefined;
/** Specify whether the checkbox is indeterminate */
export let indeterminate = false;
/** Set to `true` to display the skeleton state */
export let skeleton = false;
/** Set to `true` to mark the field as required */
export let required = false;
/** Set to `true` for the checkbox to be read-only */
export let readonly = false;
/** Set to `true` to disable the checkbox */
export let disabled = false;
/** Specify the label text */
export let labelText = '';
/** Set to `true` to visually hide the label text */
export let hideLabel = false;
/** Set a name for the input element */
export let name = '';
/**
* Specify the title attribute for the label element
* @type {string}
*/
export let title = undefined;
/** Set an id for the input label */
export let id = 'ccs-' + Math.random().toString(36);
/** Obtain a reference to the input HTML element */
export let ref = null;
import { createEventDispatcher } from 'svelte';
import CheckboxSkeleton from './CheckboxSkeleton.svelte';
const dispatch = createEventDispatcher();
$: useGroup = Array.isArray(group);
$: checked = useGroup ? group.includes(value) : checked;
$: dispatch('check', checked);
</script>
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
{#if skeleton}
<CheckboxSkeleton {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave />
{:else}
<div
class:bx--form-item={true}
class:bx--checkbox-wrapper={true}
{...$$restProps}
on:click
on:mouseover
on:mouseenter
on:mouseleave
>
<input
bind:this={ref}
type="checkbox"
{value}
{checked}
{disabled}
{id}
{indeterminate}
{name}
{required}
{readonly}
class:bx--checkbox={true}
on:change={() => {
if (useGroup) {
group = group.includes(value)
? group.filter((_value) => _value !== value)
: [...group, value];
} else {
checked = !checked;
}
}}
on:change
on:blur
/>
<label for={id} {title} class:bx--checkbox-label={true}>
<span class:bx--checkbox-label-text={true} class:bx--visually-hidden={hideLabel}>
<slot name="labelText">
{labelText}
</slot>
</span>
</label>
</div>
{/if}

View file

@ -0,0 +1,48 @@
<script lang="ts">
import { fade, fly } from 'svelte/transition';
import type { GameType } from '$lib/types';
export let game: GameType;
</script>
<article class="game-container" transition:fade>
<a class="thumbnail" href={game.url}>
<img width="140" height="140" src={game.thumb_url} alt={`Image of ${game.name}`} />
</a>
<div class="game-details">
<div class="game">
<div class="content">
<h2>{game.name}</h2>
<p>{game.year_published}</p>
<p>{game.players} {game.max_players === 1 ? 'player' : 'players'}</p>
<p>{game.playtime} minutes</p>
<p>Minimum Age: {game.min_age}</p>
<div class="description">{@html game.description}</div>
</div>
</div>
</div>
</article>
<style>
.thumbnail {
align-self: start;
}
img {
border-radius: 10px;
}
.game-container:hover {
background-color: var(--primary);
}
.game-container {
display: grid;
grid-template-columns: min-content 1fr;
gap: var(--spacing-16);
padding: var(--spacing-16) var(--spacing-16);
transition: all 0.3s;
border-radius: 8px;
}
</style>

View file

@ -0,0 +1,131 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import {
Listbox,
ListboxButton,
ListboxOptions,
ListboxOption
} from '@rgossiaux/svelte-headlessui';
const shows = [
{ id: 1, name: 'Cowboy Bebop', completed: false },
{ id: 2, name: 'Naruto', completed: false },
{ id: 3, name: 'One Piece', completed: false },
{ id: 4, name: 'Fullmetal Alchemist', completed: true },
{ id: 5, name: 'One Punch Man', completed: true },
{ id: 6, name: 'Death Note', completed: true }
];
let selected = shows[0];
</script>
<h4>Listbox</h4>
<div class="listbox">
<Listbox value={selected} on:change={(event) => (selected = event.detail)} let:open>
<ListboxButton class="button">
<span>{selected.name}</span>
<svg
width="20"
height="20"
class="arrows"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</ListboxButton>
{#if open}
<div transition:fade={{ duration: 200 }}>
<ListboxOptions class="options">
{#each shows as anime (anime.id)}
<ListboxOption
class="option"
value={anime}
disabled={anime.completed}
let:active
let:selected
>
<span class:active class:selected>{anime.name}</span>
</ListboxOption>
{/each}
</ListboxOptions>
</div>
{/if}
</Listbox>
</div>
<!-- ... -->
<style>
.listbox {
max-width: 280px;
position: relative;
font-weight: 500;
color: hsl(220, 20%, 98%);
}
.listbox :global(.button) {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
font-family: inherit;
font-size: inherit;
color: inherit;
background-color: hsl(220, 20%, 2%);
border: none;
border-radius: 10px;
}
.listbox :global(.arrows) {
width: 20px;
height: 20px;
display: block;
}
.listbox :global(.options) {
position: absolute;
top: 44px;
right: 0;
left: 0;
padding: 1rem;
background-color: hsl(220, 20%, 4%);
border-radius: 10px;
list-style: none;
}
.listbox :global(.option) {
padding: 0.8rem 0.4rem;
cursor: pointer;
}
.listbox :global(.option[aria-disabled='true']) {
color: hsl(220, 20%, 30%);
}
.listbox :global(.active) {
color: hsl(220, 80%, 70%);
}
.listbox :global(.active)::before {
content: '👉️ ';
}
.listbox :global(.selected) {
font-weight: 700;
}
.listbox :global(.selected)::before {
content: '⭐️ ';
}
</style>

View file

@ -0,0 +1,147 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import { browser } from '$app/env';
import {
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions
} from '@rgossiaux/svelte-headlessui';
const themes = {
'🌛 Night': { name: '🌛 Night' },
'☀️ Daylight': { name: '☀️ Daylight' },
'😎 Synthwave': { name: '😎 Synthwave' }
};
let selectedTheme = getTheme() ?? themes['🌛 Night'];
function getTheme() {
if (!browser) return;
const htmlElement = document.documentElement;
const userTheme = localStorage.theme;
const prefersDarkMode = window.matchMedia('prefers-color-scheme: dark').matches;
const prefersLightMode = window.matchMedia('prefers-color-scheme: light').matches;
// check if the user set a theme
if (userTheme) {
htmlElement.dataset.theme = userTheme;
return themes[userTheme];
}
// otherwise check for user preference
if (!userTheme && prefersDarkMode) {
htmlElement.dataset.theme = '🌛 Night';
localStorage.theme = '🌛 Night';
}
if (!userTheme && prefersLightMode) {
htmlElement.dataset.theme = '☀️ Daylight';
localStorage.theme = '☀️ Daylight';
}
// if nothing is set default to dark mode
if (!userTheme && !prefersDarkMode && !prefersLightMode) {
htmlElement.dataset.theme = '🌛 Night';
localStorage.theme = '🌛 Night';
}
return themes[userTheme];
}
function handleChange(event: CustomEvent) {
selectedTheme = themes[event.detail.name];
const htmlElement = document.documentElement;
htmlElement.dataset.theme = selectedTheme.name;
localStorage.theme = selectedTheme.name;
}
</script>
<div class="theme">
<span>Theme</span>
<div class="listbox">
<Listbox value={selectedTheme} on:change={handleChange} let:open>
<ListboxButton class="button">
<span>{selectedTheme.name}</span>
<span>
<svg
width="20"
height="20"
class="arrows"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</span>
</ListboxButton>
{#if open}
<div transition:fade={{ duration: 100 }}>
<ListboxOptions class="options" static>
{#each Object.entries(themes) as [key, theme] (key)}
<ListboxOption value={theme} let:active let:selected>
<span class="option" class:active class:selected>
{theme.name}
</span>
</ListboxOption>
{/each}
</ListboxOptions>
</div>
{/if}
</Listbox>
</div>
</div>
<style>
.listbox {
--width: 184px;
}
.listbox :global(.button) {
width: var(--width);
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-16) var(--spacing-24);
font-weight: 700;
background-color: var(--clr-primary);
color: var(--clr-theme-txt);
border-radius: var(--rounded-20);
box-shadow: var(--shadow-sm);
}
.listbox :global(.arrows) {
width: 20px;
height: 20px;
display: block;
}
.listbox :global(.options) {
width: var(--width);
position: absolute;
margin-top: 0.4rem;
font-weight: 700;
color: var(--clr-theme-txt);
background-color: var(--clr-primary);
border-radius: var(--rounded-20);
box-shadow: var(--shadow-sm);
list-style: none;
}
.listbox :global(.option) {
display: block;
padding: var(--spacing-16) var(--spacing-24);
border-radius: var(--rounded-20);
cursor: pointer;
}
.listbox :global(.active) {
background-color: var(--clr-theme-active);
}
</style>

View file

@ -0,0 +1,66 @@
<script>
import { Switch } from '@rgossiaux/svelte-headlessui';
let enabled = false;
</script>
<Switch
checked={enabled}
on:change={(e) => (enabled = e.detail)}
class={enabled ? 'switch switch-enabled' : 'switch switch-disabled'}
>
<span class="sr-only">Dark Mode</span>
<span class="toggle" class:toggle-on={enabled} class:toggle-off={!enabled} />
</Switch>
<style>
:global(.switch) {
position: relative;
display: inline-flex;
align-items: center;
border-radius: 1rem;
border: 0;
height: 1.25rem;
width: 2.5rem;
}
:global(.switch-enabled) {
/* Blue */
background-color: hsla(0, 0%, 0%, 1);
}
:global(.switch-disabled) {
/* Gray */
background-color: hsla(0, 0%, 100%, 0.5);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.toggle {
display: inline-block;
width: 1rem;
height: 1rem;
background-color: hsla(0, 0%, 100%, 1);
border-radius: 1rem;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
transition-property: transform;
}
.toggle-on {
transform: translateX(1.4rem);
}
.toggle-off {
transform: translateX(0.1rem);
}
</style>