-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #295 from dmlb/dark-mode-toggle
dark mode toggle, modularization into Svelte components, filter "and/or" mode
- Loading branch information
Showing
17 changed files
with
665 additions
and
401 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<script lang="ts"> | ||
// DESIGN | ||
export let version: "hollow" | "filled" = "hollow"; | ||
export let color: "green" | "red" = "green"; | ||
export let isCircle: boolean = false; | ||
export let extraClasses: string = ""; | ||
// button el | ||
export let disabled: boolean = false; | ||
export let type: "button" | "reset" | "submit" = "button"; | ||
// anchor el | ||
export let link: boolean = false; | ||
export let url: string = "#"; | ||
export let newTab: boolean = false; | ||
export let download: boolean = false; | ||
let target: "_blank" | undefined; | ||
let rel: string | undefined; | ||
if (newTab) { | ||
target = "_blank"; | ||
rel = "noreferrer"; | ||
} | ||
let designClasses: string = isCircle ? "rounded-full" : "rounded-lg"; | ||
const designClassesMap = { | ||
hollow: { | ||
green: "border-green-500 dark:border-green-700 text-green-700 dark:text-green-500 hover:text-black hover:bg-green-500 dark:hover:text-white dark:hover:bg-green-900", | ||
red: "border-red-500 dark:border-red-700 text-red-500 dark:text-red-400 hover:bg-red-500 hover:text-black dark:hover:text-white dark:hover:bg-red-900", | ||
}, | ||
filled: { | ||
green: "bg-green-700 text-white dark:bg-green-900/75 border-green-700 hover:border-green-500 dark:hover:border-green-700 dark:border-green-900/75", | ||
red: "bg-red-700 text-white dark:bg-red-900/75 border-red-700 hover:border-red-500 dark:hover:border-red-700 dark:border-red-900/75", | ||
}, | ||
}; | ||
designClasses = `${designClasses} ${designClassesMap[version][color]}`; | ||
</script> | ||
|
||
{#if link} | ||
<a | ||
class="btn-base {designClasses} {extraClasses}" | ||
href={url} | ||
{target} | ||
{rel} | ||
{download} | ||
><slot name="icon" /> | ||
<slot name="label" /> | ||
{#if newTab}<span class="sr-only">in a new tab</span>{/if} | ||
</a> | ||
{:else} | ||
<button | ||
{type} | ||
{disabled} | ||
on:click | ||
class="btn-base {designClasses} {extraClasses} disabled:text-zinc-400 disabled:border-current disabled:bg-zinc-200 dark:disabled:bg-zinc-700 disabled:hover:text-zinc-400 disabled:cursor-not-allowed" | ||
> | ||
<slot name="icon" /> | ||
<slot name="label" /> | ||
</button> | ||
{/if} | ||
|
||
<style> | ||
.btn-base { | ||
@apply inline-flex items-center gap-1; | ||
@apply border-2 p-2; | ||
@apply transition-colors; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<script lang="ts"> | ||
const removeWhitespace = (str: string) => { | ||
return str.replace(/\W+/g, ""); | ||
}; | ||
export let name: string; | ||
export let id: string | undefined = undefined; | ||
export let checked: boolean; | ||
let checkboxId: string = id ? id : removeWhitespace(name); | ||
</script> | ||
|
||
<label | ||
class="cursor-pointer py-2 px-3 rounded-full flex items-center gap-2 text-sm" | ||
for={checkboxId} | ||
> | ||
<input | ||
type="checkbox" | ||
class="appearance-none cursor-pointer w-6 h-6 rounded-full bg-white dark:bg-black checked:bg-zinc-800 dark:checked:bg-green-600 transition duration-200" | ||
bind:checked | ||
id={checkboxId} | ||
name={checkboxId} | ||
/> | ||
<span> | ||
{name} | ||
<slot /> | ||
</span> | ||
</label> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<script lang="ts"> | ||
import { browser } from "$app/environment"; | ||
export let cssClass: string; | ||
let darkMode: boolean; | ||
$: darkMode = browser | ||
? localStorage.theme === "dark" || | ||
(!("theme" in localStorage) && | ||
window.matchMedia("(prefers-color-scheme: dark)").matches) | ||
: false; | ||
const setColorMode = () => { | ||
if (darkMode) { | ||
document.documentElement.classList.remove("dark"); | ||
darkMode = false; | ||
if (browser) { | ||
localStorage.theme = "light"; | ||
} | ||
} else { | ||
document.documentElement.classList.add("dark"); | ||
darkMode = true; | ||
if (browser) { | ||
localStorage.theme = "dark"; | ||
} | ||
} | ||
return; | ||
}; | ||
</script> | ||
|
||
<button type="button" role="switch" aria-checked={darkMode} class={cssClass} on:click={setColorMode}> | ||
{#if darkMode} | ||
<!-- sun-fill icon --> | ||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-sun-fill" viewBox="0 0 16 16"> | ||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/> | ||
</svg> | ||
<span class="sr-only">Light</span> | ||
{:else} | ||
<!-- moon-stars-fill icon --> | ||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-moon-stars-fill" viewBox="0 0 16 16"> | ||
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/> | ||
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/> | ||
</svg> | ||
<span class="sr-only">Dark</span> | ||
{/if} | ||
<span class="sr-only"> Colour Scheme Mode</span> | ||
</button> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
<script lang="ts"> | ||
import { createEventDispatcher } from "svelte"; | ||
import type { FilterOption, FilterLogic, CustomFilterEvent } from "$lib/interfaces"; | ||
import Collapsible from "$lib/components/Collapsible.svelte"; | ||
import TagWrapper from "$lib/components/TagWrapper.svelte"; | ||
import Checkbox from "$lib/components/Checkbox.svelte"; | ||
import ButtonLinks from "$lib/components/ButtonLinks.svelte"; | ||
const dispatch = createEventDispatcher<CustomFilterEvent>(); | ||
let form: HTMLFormElement; | ||
export let filterOptions: FilterOption[]; | ||
export let showFilterLogic: boolean = true; | ||
// Whether all the selected tags must match the resource (vs any of the selected tags) | ||
export let filterLogicAnd: boolean = true; | ||
let filterLogic: FilterLogic | ||
$: filterLogic = filterLogicAnd ? "and" : "or"; | ||
let isFilterDirty: boolean; | ||
$: isFilterDirty = filterOptions.some( | ||
(option: FilterOption) => option.active === true | ||
); | ||
const resetFilters = () => { | ||
filterOptions.forEach(option => option.active = false) | ||
dispatch("filter", {filterOptions, filterLogic}) | ||
} | ||
const onSubmit = () => { | ||
dispatch("filter", {filterOptions, filterLogic}) | ||
} | ||
</script> | ||
|
||
{#if filterOptions} | ||
<Collapsible label="Filter"> | ||
<form | ||
bind:this={form} | ||
on:submit|preventDefault={onSubmit} | ||
class="p-4 space-y-4" | ||
> | ||
<div class="flex flex-row flex-wrap gap-2"> | ||
{#each filterOptions as filterOption} | ||
<!-- checkboxes --> | ||
<TagWrapper | ||
tagColor={filterOption.color} | ||
extraClasses="input-wrapper-focus flex justify-between gap-2" | ||
> | ||
<Checkbox | ||
name={filterOption.name} | ||
bind:checked={filterOption.active} | ||
> | ||
<span | ||
class="text-zinc-700 dark:text-zinc-300 italic" | ||
>({filterOption.count})</span | ||
> | ||
</Checkbox> | ||
</TagWrapper> | ||
{/each} | ||
</div> | ||
<div class="flex gap-2 justify-end"> | ||
{#if showFilterLogic} | ||
<label aria-label="filter with and / or" | ||
for="switch" | ||
class="inline-flex items-center rounded-md cursor-pointer outline-2 outline-offset-1 focus-within:outline text-white border-2 border-green-700 dark:border-green-900/75" | ||
> | ||
<input | ||
bind:checked={filterLogicAnd} | ||
aria-checked={filterLogicAnd} | ||
id="switch" | ||
role="switch" | ||
type="checkbox" | ||
class="opacity-0 absolute peer" | ||
/> | ||
<span | ||
class="px-4 py-3 rounded-l-sm | ||
bg-white text-zinc-500 dark:text-zinc-400 dark:bg-zinc-800 peer-checked:bg-green-700 peer-checked:text-white dark:peer-checked:bg-green-900/75" | ||
> | ||
<!-- intersect icon --> | ||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-intersect" viewBox="0 0 16 16"> | ||
<path d="M0 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2H2a2 2 0 0 1-2-2V2zm5 10v2a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1h-2v5a2 2 0 0 1-2 2H5zm6-8V2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2V6a2 2 0 0 1 2-2h5z"/> | ||
</svg><span class="sr-only">and</span></span | ||
> | ||
<span | ||
class="px-4 py-3 rounded-r-sm | ||
bg-green-700 dark:bg-green-900/75 | ||
peer-checked:text-zinc-500 peer-checked:bg-white dark:peer-checked:bg-zinc-800 dark:peer-checked:text-zinc-400" | ||
> | ||
<!-- union icon --> | ||
|
||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-union" viewBox="0 0 16 16"> | ||
<path d="M0 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-2H2a2 2 0 0 1-2-2V2z"/> | ||
</svg><span class="sr-only">or</span></span | ||
> | ||
</label> | ||
{/if} | ||
<ButtonLinks | ||
type="reset" | ||
on:click={resetFilters} | ||
disabled={!isFilterDirty} | ||
version="filled" | ||
color="green" | ||
> | ||
<svg | ||
slot="icon" | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
stroke-width="1.5" | ||
stroke="currentColor" | ||
class="w-6 h-6 inline" | ||
> | ||
<path | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" | ||
/> | ||
</svg> | ||
<span slot="label">Clear All</span> | ||
</ButtonLinks> | ||
|
||
<ButtonLinks type="submit" version="filled" color="green"> | ||
<svg | ||
slot="icon" | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill="none" | ||
viewBox="0 0 24 24" | ||
stroke-width="1.5" | ||
stroke="currentColor" | ||
class="w-6 h-6 inline" | ||
> | ||
<path | ||
stroke-linecap="round" | ||
stroke-linejoin="round" | ||
d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 01-.659 1.591l-5.432 5.432a2.25 2.25 0 00-.659 1.591v2.927a2.25 2.25 0 01-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 00-.659-1.591L3.659 7.409A2.25 2.25 0 013 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0112 3z" | ||
/> | ||
</svg> | ||
<span slot="label">Filter</span> | ||
</ButtonLinks> | ||
</div> | ||
</form> | ||
</Collapsible> | ||
{/if} |
Oops, something went wrong.