Skip to content

Commit

Permalink
component: Switch (#51)
Browse files Browse the repository at this point in the history
Co-authored-by: Davis SHYAKA <87414827+davis-shyaka@users.noreply.github.com>
  • Loading branch information
shyakadavis and shyakadavis authored Aug 13, 2024
1 parent 96a0def commit b62d435
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/lib/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import Information from './information.svg?component';
import Key from './key.svg?component';
import Link from './link.svg?component';
import LoaderCircle from './loader-circle.svg?component';
import LockClosedSmall from './lock-closed-small.svg?component';
import LockOpenSmall from './lock-open-small.svg?component';
import LogoBitbucketColor from './logo-bitbucket-color.svg?component';
import LogoGeist from './logo-geist.svg?component';
import LogoGithub from './logo-github.svg?component';
Expand Down Expand Up @@ -106,6 +108,8 @@ export const Icons = {
Key,
Link,
LoaderCircle,
LockClosedSmall,
LockOpenSmall,
LogoBitbucketColor,
LogoGeist,
LogoGithub,
Expand Down
3 changes: 3 additions & 0 deletions src/lib/assets/icons/lock-closed-small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/lib/assets/icons/lock-open-small.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions src/lib/components/ui/switch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Icons } from '$lib/assets/icons';
import { Switch as SwitchPrimitive } from 'bits-ui';
import { tv, type VariantProps } from 'tailwind-variants';
import Root from './switch.svelte';

export const switch_variants = tv({
base: 'group inline-flex shrink-0 cursor-pointer items-center rounded-full border border-gray-alpha-400 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-color focus-visible:ring-offset-2 focus-visible:ring-offset-background-200 disabled:cursor-not-allowed data-[state=unchecked]:bg-gray-100 disabled:data-[state=unchecked]:bg-background-100',
variants: {
variant: {
default:
'data-[state=checked]:bg-blue-700 disabled:data-[state=checked]:border-accents-2 disabled:data-[state=checked]:bg-accents-1',
subtle: 'data-[state=checked]:bg-gray-100 disabled:data-[state=checked]:bg-background-100'
},
size: {
md: 'h-[14px] w-[28px]',
lg: 'h-[24px] w-[40px]'
}
},
defaultVariants: {
variant: 'default',
size: 'md'
}
});

type Variant = VariantProps<typeof switch_variants>['variant'];
type Size = VariantProps<typeof switch_variants>['size'];

export type Props = SwitchPrimitive.Props & {
variant?: Variant;
size?: Size;
icon?: {
checked: typeof Icons.Accessibility;
unchecked: typeof Icons.Accessibility;
};
direction?: 'switch-first' | 'switch-last';
};

export { Root, Root as Switch };
75 changes: 75 additions & 0 deletions src/lib/components/ui/switch/switch.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Switch as SwitchPrimitive } from 'bits-ui';
import { switch_variants, type Props } from '.';
type $$Props = Props;
type $$Events = SwitchPrimitive.Events;
let class_name: $$Props['class'] = undefined;
export let checked: $$Props['checked'] = undefined;
export let variant: $$Props['variant'] = 'default';
export let size: $$Props['size'] = 'md';
export let icon: $$Props['icon'] = undefined;
export let direction: $$Props['direction'] = 'switch-last';
export { class_name as class };
</script>

<label class="flex items-center gap-2">
{#if direction === 'switch-last'}
<span class="whitespace-nowrap text-xs font-medium text-accents-5">
<slot></slot>
</span>
{/if}
<SwitchPrimitive.Root
bind:checked
class={cn(switch_variants({ variant, size, className: class_name }), class_name)}
{...$$restProps}
on:click
on:keydown
>
{#if icon}
<SwitchPrimitive.Thumb asChild let:attrs>
<span
class={cn(
'pointer-events-none grid place-items-center rounded-full bg-gray-700 text-background-100 ring-0 transition-transform [box-shadow:_0_0_4px_rgba(0,0,0,.12),0_1px_1px_rgba(0,0,0,.16)] data-[state=unchecked]:translate-x-0 data-[state=checked]:bg-gray-1000 group-data-[disabled=true]:bg-gray-200 group-data-[disabled=true]:text-gray-700',
{
'size-3 data-[state=checked]:translate-x-[13.5px]': size === 'md',
'size-[22px] data-[state=checked]:translate-x-[15.5px]': size === 'lg'
}
)}
{...attrs}
>
{#if checked}
<svelte:component
this={icon.checked}
aria-hidden="true"
class="size-full max-h-4 max-w-4"
/>
{:else}
<svelte:component
this={icon.unchecked}
aria-hidden="true"
class="size-full max-h-4 max-w-4"
/>
{/if}
</span>
</SwitchPrimitive.Thumb>
{:else}
<SwitchPrimitive.Thumb
class={cn(
'pointer-events-none block rounded-full bg-gray-700 ring-0 transition-transform [box-shadow:_0_0_4px_rgba(0,0,0,.12),0_1px_1px_rgba(0,0,0,.16)] data-[state=unchecked]:translate-x-0 data-[state=checked]:bg-gray-1000 group-data-[disabled=true]:bg-gray-200',
{
'size-3 data-[state=checked]:translate-x-[13.5px]': size === 'md',
'size-[22px] data-[state=checked]:translate-x-[15.5px]': size === 'lg'
}
)}
/>
{/if}
</SwitchPrimitive.Root>
{#if direction === 'switch-first'}
<span class="whitespace-nowrap text-xs font-medium text-accents-5">
<slot></slot>
</span>
{/if}
</label>
2 changes: 1 addition & 1 deletion src/lib/config/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export const aside_items: Aside = {
{
title: 'Switch',
href: '/switch',
status: 'soon'
status: 'new'
},
{
title: 'Table',
Expand Down
39 changes: 38 additions & 1 deletion src/routes/switch/+page.svelte
Original file line number Diff line number Diff line change
@@ -1 +1,38 @@
<h1>switch</h1>
<script lang="ts">
import Demo from '$lib/components/shared/demo.svelte';
import PageWrapper from '$lib/components/shared/page-wrapper.svelte';
import Default from './default.svelte';
import default_code from './default.svelte?raw';
import Disabled from './disabled.svelte';
import disabled_code from './disabled.svelte?raw';
import Sizes from './sizes.svelte';
import sizes_code from './sizes.svelte?raw';
import SubtleWithIcon from './subtle-with-icon.svelte';
import subtle_with_icon_code from './subtle-with-icon.svelte?raw';
import WithLabel from './with-label.svelte';
import with_label from './with-label.svelte?raw';
export let data;
</script>

<PageWrapper title={data.title} description={data.description}>
<Demo id="default" code={default_code}>
<Default />
</Demo>

<Demo id="disabled" code={disabled_code}>
<Disabled />
</Demo>

<Demo id="sizes" code={sizes_code}>
<Sizes />
</Demo>

<Demo id="subtle-with-icon" code={subtle_with_icon_code}>
<SubtleWithIcon />
</Demo>

<Demo id="with-label" code={with_label}>
<WithLabel />
</Demo>
</PageWrapper>
21 changes: 21 additions & 0 deletions src/routes/switch/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { MetaTagsProps } from 'svelte-meta-tags';

export function load() {
const title = 'Switch';
const description = 'Displays a boolean value.';

const pageMetaTags = Object.freeze({
title,
description,
openGraph: {
title,
description
}
}) satisfies MetaTagsProps;

return {
pageMetaTags,
title,
description
};
}
8 changes: 8 additions & 0 deletions src/routes/switch/default.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import { Switch } from '$lib/components/ui/switch';
</script>

<div class="grid gap-6">
<Switch aria-label="Enable Firewall" />
<Switch aria-label="Enable Firewall" checked />
</div>
8 changes: 8 additions & 0 deletions src/routes/switch/disabled.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script lang="ts">
import { Switch } from '$lib/components/ui/switch';
</script>

<div class="grid gap-6">
<Switch aria-label="Enable Firewall" disabled />
<Switch aria-label="Enable Firewall" checked disabled />
</div>
10 changes: 10 additions & 0 deletions src/routes/switch/sizes.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import { Switch } from '$lib/components/ui/switch';
let checked = false;
</script>

<div class="grid grid-cols-2">
<Switch aria-label="Enable Firewall" bind:checked />
<Switch aria-label="Enable Firewall" bind:checked size="lg" />
</div>
38 changes: 38 additions & 0 deletions src/routes/switch/subtle-with-icon.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts">
import { Icons } from '$lib/assets/icons';
import { Switch } from '$lib/components/ui/switch';
let checked = false;
</script>

<div class="grid gap-6">
<Switch
bind:checked
variant="subtle"
aria-label="Enable Firewall"
icon={{ checked: Icons.LockClosedSmall, unchecked: Icons.LockOpenSmall }}
/>
<Switch
bind:checked
disabled
variant="subtle"
aria-label="Enable Firewall"
icon={{ checked: Icons.LockClosedSmall, unchecked: Icons.LockOpenSmall }}
/>

<Switch
bind:checked
variant="subtle"
aria-label="Enable Firewall"
size="lg"
icon={{ checked: Icons.LockClosedSmall, unchecked: Icons.LockOpenSmall }}
/>
<Switch
bind:checked
disabled
variant="subtle"
aria-label="Enable Firewall"
size="lg"
icon={{ checked: Icons.LockClosedSmall, unchecked: Icons.LockOpenSmall }}
/>
</div>
78 changes: 78 additions & 0 deletions src/routes/switch/with-label.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<script lang="ts">
import { Icons } from '$lib/assets/icons';
import { Switch } from '$lib/components/ui/switch';
let checked = false;
const Closed = Icons.LockClosedSmall;
const Open = Icons.LockOpenSmall;
</script>

<div class="grid w-fit grid-cols-2 gap-6">
<Switch bind:checked>Enable Firewall</Switch>
<Switch bind:checked direction="switch-first">Enable Firewall</Switch>

<Switch bind:checked size="lg">Enable Firewall</Switch>
<Switch bind:checked direction="switch-first" size="lg">Enable Firewall</Switch>

<Switch bind:checked icon={{ checked: Closed, unchecked: Open }}>Enable Firewall</Switch>
<Switch bind:checked direction="switch-first" icon={{ checked: Closed, unchecked: Open }}>
Enable Firewall
</Switch>

<Switch bind:checked size="lg" icon={{ checked: Closed, unchecked: Open }}>
Enable Firewall
</Switch>
<Switch
bind:checked
direction="switch-first"
size="lg"
icon={{ checked: Closed, unchecked: Open }}
>
Enable Firewall
</Switch>

<Switch bind:checked variant="subtle" icon={{ checked: Closed, unchecked: Open }}>
Enable Firewall
</Switch>
<Switch
bind:checked
direction="switch-first"
variant="subtle"
icon={{ checked: Closed, unchecked: Open }}
>
Enable Firewall
</Switch>

<Switch bind:checked size="lg" variant="subtle" icon={{ checked: Closed, unchecked: Open }}>
Enable Firewall
</Switch>
<Switch
bind:checked
direction="switch-first"
size="lg"
variant="subtle"
icon={{ checked: Closed, unchecked: Open }}
>
Enable Firewall
</Switch>

<Switch
bind:checked
size="lg"
variant="subtle"
disabled
icon={{ checked: Closed, unchecked: Open }}
>
Enable Firewall
</Switch>
<Switch
bind:checked
direction="switch-first"
size="lg"
variant="subtle"
disabled
icon={{ checked: Closed, unchecked: Open }}
>
Enable Firewall
</Switch>
</div>

0 comments on commit b62d435

Please sign in to comment.