mirror of
https://github.com/youwen5/site.git
synced 2024-11-24 17:33:51 -08:00
feat: add dropdown to color picker with 'system' option
This commit is contained in:
parent
a6c261cb22
commit
a40b96329c
16 changed files with 405 additions and 95 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -2,10 +2,10 @@
|
||||||
import Name from '$lib/assets/Name.svelte';
|
import Name from '$lib/assets/Name.svelte';
|
||||||
import { HamburgerMenu } from 'svelte-radix';
|
import { HamburgerMenu } from 'svelte-radix';
|
||||||
import { Drawer } from 'vaul-svelte';
|
import { Drawer } from 'vaul-svelte';
|
||||||
import { toggleMode } from 'mode-watcher';
|
|
||||||
import Separator from '../ui/separator/separator.svelte';
|
import Separator from '../ui/separator/separator.svelte';
|
||||||
import Button from '../ui/button/button.svelte';
|
import Button from '../ui/button/button.svelte';
|
||||||
import { Sun, Moon, Home, Person, File, Backpack } from 'svelte-radix';
|
import { Sun, Moon, Home, Person, File, Backpack } from 'svelte-radix';
|
||||||
|
import ThemePicker from '../ThemePicker.svelte';
|
||||||
|
|
||||||
let open: boolean = false;
|
let open: boolean = false;
|
||||||
|
|
||||||
|
@ -74,19 +74,13 @@
|
||||||
>
|
>
|
||||||
</Drawer.Close>
|
</Drawer.Close>
|
||||||
<Separator class="h-1 rounded-3xl mt-1 dark:bg-zinc-500 my-2" />
|
<Separator class="h-1 rounded-3xl mt-1 dark:bg-zinc-500 my-2" />
|
||||||
<Drawer.Close asChild let:builder>
|
<ThemePicker let:builder>
|
||||||
<Button
|
<Button variant="outline" size="lg" builders={[builder]}>
|
||||||
variant="outline"
|
|
||||||
class="bg-accent dark:border-zinc-500"
|
|
||||||
size="lg"
|
|
||||||
on:click={toggleMode}
|
|
||||||
builders={[builder]}
|
|
||||||
>
|
|
||||||
<Sun class="mr-4 dark:hidden" />
|
<Sun class="mr-4 dark:hidden" />
|
||||||
<Moon class="mr-4 hidden dark:block" />
|
<Moon class="mr-4 hidden dark:block" />
|
||||||
Toggle Theme
|
Change Theme
|
||||||
</Button>
|
</Button>
|
||||||
</Drawer.Close>
|
</ThemePicker>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mx-auto mb-8 h-1.5 w-12 flex-shrink-0 rounded-full bg-zinc-300" />
|
<div class="mx-auto mb-8 h-1.5 w-12 flex-shrink-0 rounded-full bg-zinc-300" />
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Sun, Moon } from 'svelte-radix';
|
|
||||||
import Button from '../ui/button/button.svelte';
|
|
||||||
import { toggleMode } from 'mode-watcher';
|
|
||||||
import Name from '$lib/assets/Name.svelte';
|
import Name from '$lib/assets/Name.svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { navigating } from '$app/stores';
|
import { navigating } from '$app/stores';
|
||||||
import Coredump from '$lib/assets/Coredump.svelte';
|
import Coredump from '$lib/assets/Coredump.svelte';
|
||||||
import Separator from '../ui/separator/separator.svelte';
|
import Separator from '../ui/separator/separator.svelte';
|
||||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
|
||||||
import Drawer from './Drawer.svelte';
|
import Drawer from './Drawer.svelte';
|
||||||
|
import ThemePicker from '../ThemePicker.svelte';
|
||||||
|
import Button from '../ui/button/button.svelte';
|
||||||
|
import { Moon, Sun } from 'svelte-radix';
|
||||||
|
|
||||||
let current: 'blog' | 'about' | 'home' | 'portfolio' | string;
|
let current: 'blog' | 'about' | 'home' | 'portfolio' | string;
|
||||||
|
|
||||||
|
@ -56,25 +55,13 @@
|
||||||
class="text-lg sm:text-xl md:text-2xl font-medium text-primary px-4 py-1 rounded-3xl hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors duration-200"
|
class="text-lg sm:text-xl md:text-2xl font-medium text-primary px-4 py-1 rounded-3xl hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors duration-200"
|
||||||
class:selected={current === 'blog'}>Blog</a
|
class:selected={current === 'blog'}>Blog</a
|
||||||
>
|
>
|
||||||
<Tooltip.Root openDelay={500}>
|
<ThemePicker let:builder>
|
||||||
<Tooltip.Trigger asChild let:builder>
|
<Button builders={[builder]} variant="outline" size="icon" class="my-1">
|
||||||
<Button
|
|
||||||
builders={[builder]}
|
|
||||||
on:click={toggleMode}
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
class="my-1"
|
|
||||||
>
|
|
||||||
<Sun class="dark:hidden" />
|
<Sun class="dark:hidden" />
|
||||||
<Moon class="hidden dark:block" />
|
<Moon class="hidden dark:block" />
|
||||||
<span class="sr-only">Toggle theme</span>
|
<span class="sr-only">Toggle theme</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip.Trigger>
|
</ThemePicker>
|
||||||
<Tooltip.Content>
|
|
||||||
<p class="dark:hidden">Too bright? Switch to dark mode</p>
|
|
||||||
<p class="hidden dark:block">Too dark? Turn on the lights</p>
|
|
||||||
</Tooltip.Content>
|
|
||||||
</Tooltip.Root>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
36
src/lib/components/ThemePicker.svelte
Normal file
36
src/lib/components/ThemePicker.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<!--
|
||||||
|
@component @name ThemePicker
|
||||||
|
|
||||||
|
@description - A wrapper for a dropdown menu that allows the user to
|
||||||
|
change the color theme of the website.
|
||||||
|
|
||||||
|
@slot builder - A slot for the button that triggers the dropdown menu.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||||
|
import { setMode } from 'mode-watcher';
|
||||||
|
|
||||||
|
let modes = [
|
||||||
|
{ value: 'light', label: 'Light' },
|
||||||
|
{ value: 'dark', label: 'Dark' },
|
||||||
|
{ value: 'system', label: 'System' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const changeMode = (mode: string) => {
|
||||||
|
setMode(mode as 'light' | 'dark' | 'system');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger asChild let:builder>
|
||||||
|
<slot {builder} />
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Content>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
{#each modes as { value, label } (value)}
|
||||||
|
<DropdownMenu.Item on:click={() => changeMode(value)}>{label}</DropdownMenu.Item>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import Check from "svelte-radix/Check.svelte";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.CheckboxItemProps;
|
||||||
|
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let checked: $$Props["checked"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.CheckboxItem
|
||||||
|
bind:checked
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerdown
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuPrimitive.CheckboxIndicator>
|
||||||
|
<Check class="h-4 w-4" />
|
||||||
|
</DropdownMenuPrimitive.CheckboxIndicator>
|
||||||
|
</span>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.CheckboxItem>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.ContentProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let sideOffset: $$Props["sideOffset"] = 4;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Content
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
{sideOffset}
|
||||||
|
class={cn(
|
||||||
|
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:keydown
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.Content>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.ItemProps & {
|
||||||
|
inset?: boolean;
|
||||||
|
};
|
||||||
|
type $$Events = DropdownMenuPrimitive.ItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let inset: $$Props["inset"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Item
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerdown
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.Item>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.LabelProps & {
|
||||||
|
inset?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let inset: $$Props["inset"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Label
|
||||||
|
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.Label>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.RadioGroupProps;
|
||||||
|
|
||||||
|
export let value: $$Props["value"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.RadioGroup>
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import DotFilled from "svelte-radix/DotFilled.svelte";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.RadioItemProps;
|
||||||
|
type $$Events = DropdownMenuPrimitive.RadioItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let value: DropdownMenuPrimitive.RadioItemProps["value"];
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.RadioItem
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{value}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerdown
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<DropdownMenuPrimitive.RadioIndicator>
|
||||||
|
<DotFilled class="h-4 w-4 fill-current" />
|
||||||
|
</DropdownMenuPrimitive.RadioIndicator>
|
||||||
|
</span>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.RadioItem>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.SeparatorProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.Separator
|
||||||
|
class={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</span>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.SubContentProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
x: -10,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.SubContent
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:keydown
|
||||||
|
on:focusout
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DropdownMenuPrimitive.SubContent>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import ChevronRight from "svelte-radix/ChevronRight.svelte";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
type $$Props = DropdownMenuPrimitive.SubTriggerProps & {
|
||||||
|
inset?: boolean;
|
||||||
|
};
|
||||||
|
type $$Events = DropdownMenuPrimitive.SubTriggerEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let inset: $$Props["inset"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DropdownMenuPrimitive.SubTrigger
|
||||||
|
class={cn(
|
||||||
|
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground",
|
||||||
|
inset && "pl-8",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<ChevronRight class="ml-auto h-4 w-4" />
|
||||||
|
</DropdownMenuPrimitive.SubTrigger>
|
48
src/lib/components/ui/dropdown-menu/index.ts
Normal file
48
src/lib/components/ui/dropdown-menu/index.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
|
||||||
|
import Item from "./dropdown-menu-item.svelte";
|
||||||
|
import Label from "./dropdown-menu-label.svelte";
|
||||||
|
import Content from "./dropdown-menu-content.svelte";
|
||||||
|
import Shortcut from "./dropdown-menu-shortcut.svelte";
|
||||||
|
import RadioItem from "./dropdown-menu-radio-item.svelte";
|
||||||
|
import Separator from "./dropdown-menu-separator.svelte";
|
||||||
|
import RadioGroup from "./dropdown-menu-radio-group.svelte";
|
||||||
|
import SubContent from "./dropdown-menu-sub-content.svelte";
|
||||||
|
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
|
||||||
|
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
|
||||||
|
|
||||||
|
const Sub = DropdownMenuPrimitive.Sub;
|
||||||
|
const Root = DropdownMenuPrimitive.Root;
|
||||||
|
const Trigger = DropdownMenuPrimitive.Trigger;
|
||||||
|
const Group = DropdownMenuPrimitive.Group;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Sub,
|
||||||
|
Root,
|
||||||
|
Item,
|
||||||
|
Label,
|
||||||
|
Group,
|
||||||
|
Trigger,
|
||||||
|
Content,
|
||||||
|
Shortcut,
|
||||||
|
Separator,
|
||||||
|
RadioItem,
|
||||||
|
SubContent,
|
||||||
|
SubTrigger,
|
||||||
|
RadioGroup,
|
||||||
|
CheckboxItem,
|
||||||
|
//
|
||||||
|
Root as DropdownMenu,
|
||||||
|
Sub as DropdownMenuSub,
|
||||||
|
Item as DropdownMenuItem,
|
||||||
|
Label as DropdownMenuLabel,
|
||||||
|
Group as DropdownMenuGroup,
|
||||||
|
Content as DropdownMenuContent,
|
||||||
|
Trigger as DropdownMenuTrigger,
|
||||||
|
Shortcut as DropdownMenuShortcut,
|
||||||
|
RadioItem as DropdownMenuRadioItem,
|
||||||
|
Separator as DropdownMenuSeparator,
|
||||||
|
RadioGroup as DropdownMenuRadioGroup,
|
||||||
|
SubContent as DropdownMenuSubContent,
|
||||||
|
SubTrigger as DropdownMenuSubTrigger,
|
||||||
|
CheckboxItem as DropdownMenuCheckboxItem,
|
||||||
|
};
|
Loading…
Reference in a new issue